# Pi-native annotation loop

The Pi extension opens a dedicated Chrome preview and brings **Send** actions
back into the same conversation, including a real Chrome PNG screenshot. It
adds `/annotate`, `annotation_inspect`, and `annotation_complete`. The standalone
bookmarklet/receiver workflow remains available and independent.

## Run from this checkout

Requirements: Node `^20.19.0 || >=22.12.0`, Google Chrome installed, a current Pi
with extension support, and a vision-capable model configured in Pi.

```sh
# In the annotate-loop checkout:
npm ci

# In the project you want Pi to edit (start its dev server separately):
pi -e /absolute/path/to/annotate-loop/extensions/annotate.ts \
   --skill /absolute/path/to/annotate-loop/skills/annotate
```

In that Pi conversation:

```text
/annotate http://localhost:4321/
```

For persistent package discovery, `pi install /absolute/path/to/annotate-loop`
adds the checkout to Pi settings; restart Pi or `/reload` afterward. This does
not publish to npm. From a published version containing this extension, use
`pi install npm:annotate-loop` instead. Local path installs require the `npm ci`
step above; git/npm Pi installs install runtime dependencies automatically.

**No receiver, bookmarklet, Astro adapter, or consumer code changes are required.**
Nothing is injected into your site's build or public directory. Start Pi in the
correct consumer worktree: a localhost URL alone cannot prove which repository
serves it.

## In the preview

- The visible **Annotate · dev** dock identifies the worktree and Pi session.
- Hover and click an element. Type the desired change and **Send**.
- Under **Element details & preview**, edit text, color, background, font size,
  margin, or padding. These are temporary DOM previews. Text editing is disabled
  for elements containing child elements, preserving their markup/listeners.
- Choose **This instance** or **Shared component**, and **Current breakpoint**
  or **All sizes**. These choices guide source edits; the extension does not
  guess the application's component or responsive architecture.
- **Add to batch** captures without sending. **Send all** delivers one request
  with up to eight items; **Discard batch** drops unsent drafts.
- **Cancel** or Escape restores preview changes. Sending captures the preview
  and restores the original DOM before Pi receives the request.

Screenshots contain the current viewport and a target outline. The instruction
panel/dock are hidden during capture. Location evidence includes the full URL,
CSS path, XPath, component hint, text excerpt, viewport/scroll position, element
bounds, and relevant styles. Each image is limited to 4 MiB; one batch is limited
to 12 MiB base64-encoded. Reduce the viewport or batch size if a limit is reached.

## Delivery and review

Submissions are appended to Pi session entries before message injection. The
extension sends text plus native PNG image blocks via `pi.sendUserMessage`.
If Pi is busy, it queues a follow-up; it does not launch a worker or interrupt
another tool call. **Delivered/Queued** means Pi's API accepted the message,
not that the model has finished or that provider billing/authentication succeeded.
A synchronous delivery rejection offers **Retry delivery**. Provider failures
are handled in Pi, not retried by the browser.

Pi must call `annotation_inspect` for each target before marking a request ready.
Inspection checks the exact URL, unique CSS target, agreeing XPath, tag, and
original text against the live DOM. A mismatch requires a fresh selection or
clarification. Successful inspection is a location check, not proof of correct
implementation. Pi still needs to locate source, edit it, and run relevant checks.

After implementation, `annotation_complete` records the summary/checks and
returns a current screenshot. The browser displays **Ready to review**. Only
**Looks good** marks it resolved. **Revise** asks you to select a current element
and submits a new request linked to the original.

## Stop, reload, and session boundaries

`/annotate stop` closes only the extension's owned browser/profile. Closing the
preview window, leaving its bound origin (including changing ports), switching
to a text-only model, or stopping/reloading Pi also disconnects it. Already
submitted Pi work is not cancelled. Unsent batches are memory-only and lost
on disconnect; page reloads within the same origin retain them and reinject the
visible dock.

Opening another `/annotate <url>` closes the previous preview. Its submitted
requests stay known until Pi's extension is reloaded or the conversation changes,
but cannot be inspected against the closed window. Ask for a new selection.

**Recovery limit:** saved messages and custom entries remain in normal persisted
Pi history, but browser connections and request tool handles are not reconstructed
after restart/reload, `/new`, `/resume`, `/fork`, or tree navigation. No request is
automatically resent. Resume by opening a fresh preview and reselecting pending
targets. `pi --no-session` is memory-only: there is no durable recovery after exit.

## Safety and privacy

- Exact loopback hosts only: `localhost`, `127.0.0.1`, `[::1]`, over HTTP(S), with
  no URL credentials. One origin and one tab are bound per connection.
- The extension launches an isolated named `chrome-devtools-axi` session with a
  disposable profile. It strips inherited attach/session settings and never
  controls the default/shared browser. Normal Chrome cookies/logins do not carry
  over. The package pins its Chrome tooling; there is no runtime `npx` download.
- No web-facing prompt receiver is opened. The bridge polls its owned tab over
  CDP, uses a per-connection capability, and requires trusted UI input.
- **Trusted local previews only.** A closed shadow root and capability reduce
  accidental interference; they are not a sandbox against hostile JavaScript
  running in the page. Do not use this on untrusted apps or downloaded HTML.
- Page evidence is untrusted data. Only the explicitly submitted request and
  chosen property changes should guide edits. The agent must re-verify source.
- Screenshots, URLs (including queries), page text, and instructions enter Pi
  history and the selected model provider. Do not expose secrets or private
  content. Treat Pi session exports like screenshots; ephemeral Chrome cleanup
  does not delete session history.
- Iframe contents and shadow-tree internals are not supported selection targets.
  Full-page screenshots and automatic source-map/component resolution are not
  provided. HMR replacing a target during capture is rejected, not guessed.
- Runtime injection leaves consumer production builds untouched. Still run
  `annotate-loop-guard` after builds if you also use the badge/bookmarklet. It
  detects the Pi overlay marker too, in case injection code was copied into a
  consumer bundle accidentally.

## Tests

```sh
npm test                # behavioral DOM, extension, lifecycle, package/core tests
npm run test:browser   # real Chrome, isolated headless profile, no model billed
ANNOTATE_HEADED_TEST=1 npm run test:browser  # watch the browser test
```

The browser test uses trusted CDP input, screenshots, live inspection, review,
and reload. Pi delivery is recorded at the API boundary; it does not claim a
paid model made source edits. To dogfood model implementation, open the extension
in your real Pi project session and submit a small change for review.
