generate: plan, tune, and accept exactly like live

The lane's variants were tamer than the ones a live session makes on the
same element: its poll instructions replaced live.md's planning method
with a cheat sheet, its reference forbade knobs, told the agent to copy
the markup verbatim and to treat DESIGN.md as a hard boundary, and its
accept appended anchored overrides instead of integrating the design.
Measured on the same page with Opus, live runs promoted a tier, broke the
grid, and declared knobs; lane runs restyled three equal boxes.

Now a Go the generate verb fires gets the same _instructions as a user's
Go (the action's reference, section 4 planning, knobs per section 7),
generate.md hands the design work to live.md's Handle generate and its
Required after accept, Setup runs as for any command, the Tune chip
behaves as in any session, and the mechanical bake is opt-in (--bake)
instead of the lane's default. The start verdict points at live.md, and
`browser` (the config key the opener reads) is a recognized key.
Goldens re-recorded for the accept help and the recognized-keys line.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Abdul Wahab
2026-09-15 05:45:49 +05:00
committed by Abdul Wahab
co-authored by Claude Fable 5
parent c36e37808e
commit 695d1bd515
22 changed files with 76 additions and 161 deletions
+4 -4
View File
@@ -1480,7 +1480,7 @@ Binds `127.0.0.1:PORT`. CORS: if request has `Origin` and (origin is loopback ht
| `POST /manual-edit-discard?token=&pageUrl=` | 401 | see 10 |
| `POST /manual-edit` | | 410 `{"error":"/manual-edit is removed; use /manual-edit-stash and /manual-edit-commit for staged copy edits."}` |
| `POST /agent-target` | body JSON `token` mismatch → 401 `{"error":"Unauthorized"}`; invalid JSON → 400 `{"error":"Invalid JSON"}` | Agent-initiated targeting (the `generate` command). Validation (400 `{"error":<msg>}`, messages verbatim): `agent_target: selector is required`, `agent_target: selector too long` (>1000 chars), `agent_target: invalid action (valid: <VISUAL_ACTIONS joined ', '>)`, `agent_target: count must be 1-8`, `agent_target: text must be a string of at most 500 chars`, `agent_target: index must be a positive integer (1-based)`, `agent_target: prompt must be a string of at most 2000 chars`, `agent_target: dryRun must be a boolean` / `agent_target: hideLiveBar must be a boolean`. No SSE client → 200 `{ok:false, error:'no_browser_connected'}`. Otherwise mint an 8-hex `targetId`, broadcast `agent_target` (see 6.2), and **hold the response** until `/agent-target-result` resolves it, every connected overlay has declined (busy roll call, see `/agent-target-claim`), or `IMPECCABLE_AGENT_TARGET_TIMEOUT_MS` (default 15000) elapses: busy verdict `{ok:false, error:'busy', state, reason}` from the first report when any report exists, else `{ok:false, error:'browser_timeout', timeoutMs}`. The held reply is 200 `{targetId, ...result}`; shutdown resolves every held request with `{ok:false, error:'server_stopping'}`. |
| `POST /agent-target-result` | 401 / 400 Invalid JSON | `targetId` and `clientId` (non-empty strings) required else 400 (`agent_target_result: missing targetId` / `agent_target_result: missing clientId`); while the target is pending, only its lease holder's `clientId` may answer: another overlay gets 409 `{"error":"agent_target_result: not the holder", reason:'not_holder'|'unclaimed', targetId}` and the request stays pending. Otherwise `targetId` (non-empty string) required else 400 `{"error":"agent_target_result: missing targetId"}`; the remaining body fields (minus `token`) resolve the held request; 200 `{ok:true, delivered:boolean}` (`delivered:false` when nothing awaits that id). A `generate` event on `POST /events` may carry `agentTarget: {targetId, result}`: once the event is accepted, the server resolves that pending target with `result` (the envelope is stripped before journaling and never reaches the poller), so a page that dies between Go and its result cannot leave the request pending for a second Go elsewhere; whichever of the event and the result post lands first answers. An accepted generate event carrying the envelope is journaled and queued with `origin: "agent"`, and `live-poll` renders that event's `_instructions` as the fast path (identity from the event's `element.computedStyles` / `cssCustomProperties` / `parentContext`, the action's three dimensions, no parameter knobs unless the prompt asks, one edit, reply done) instead of the interactive planning pointer and the action-reference read. The envelope also carries `clientId`: a generate event from a page that is not the pending target's holder (another page holds the lease, or held it last, or nobody claimed it; a lapsed lease still belongs to the page that held it last until a rescuer claims), or naming a target that was already answered, with a different session or with none (a timeout or a failure verdict the CLI has reported), or that the helper neither holds nor remembers (never issued by it, or evicted from its bounded record of answered targets), is refused with 409 `{"error":"agent_target_already_served", targetId, sessionId?}` and journals nothing, and the overlay drops that local session; the holder's own Go and the answering session's own event are welcome. |
| `POST /agent-target-result` | 401 / 400 Invalid JSON | `targetId` and `clientId` (non-empty strings) required else 400 (`agent_target_result: missing targetId` / `agent_target_result: missing clientId`); while the target is pending, only its lease holder's `clientId` may answer: another overlay gets 409 `{"error":"agent_target_result: not the holder", reason:'not_holder'|'unclaimed', targetId}` and the request stays pending. Otherwise `targetId` (non-empty string) required else 400 `{"error":"agent_target_result: missing targetId"}`; the remaining body fields (minus `token`) resolve the held request; 200 `{ok:true, delivered:boolean}` (`delivered:false` when nothing awaits that id). A `generate` event on `POST /events` may carry `agentTarget: {targetId, result}`: once the event is accepted, the server resolves that pending target with `result` (the envelope is stripped before journaling and never reaches the poller), so a page that dies between Go and its result cannot leave the request pending for a second Go elsewhere; whichever of the event and the result post lands first answers. An accepted generate event carrying the envelope is journaled and queued with `origin: "agent"` (the overlay restores its lane chrome from it); its `_instructions` are the same planning steps a user's Go gets. The envelope also carries `clientId`: a generate event from a page that is not the pending target's holder (another page holds the lease, or held it last, or nobody claimed it; a lapsed lease still belongs to the page that held it last until a rescuer claims), or naming a target that was already answered, with a different session or with none (a timeout or a failure verdict the CLI has reported), or that the helper neither holds nor remembers (never issued by it, or evicted from its bounded record of answered targets), is refused with 409 `{"error":"agent_target_already_served", targetId, sessionId?}` and journals nothing, and the overlay drops that local session; the holder's own Go and the answering session's own event are welcome. |
| `POST /live-bar` | 401 / 400 Invalid JSON | `hidden` (boolean) required else 400 `{"error":"live_bar: hidden must be a boolean"}`. Sets the helper-wide bar preference; on a change broadcasts `{type:'live_bar', hidden}` to every SSE client. `GET /status` and the SSE `connected` frame carry it as `hideLiveBar`. Answers `{ok:true, hidden}`. |
| `POST /agent-target-claim` | 401 / 400 Invalid JSON | `targetId` and `clientId` (non-empty strings) required else 400 `{"error":"agent_target_claim: missing targetId or clientId"}`. Roll call plus a first-wins lease, so exactly one overlay acts on a broadcast target. Unknown or resolved target → `{ok:true, granted:false, pending:false}` (ends a rescuer's retry loop). `eligible !== true` → record `{state, reason, result?}` under `clientId` (replacing an earlier report; `result` is the overlay's resolution verdict when `reason` is `no_match`, i.e. its page cannot resolve the selector), release the lease if this client holds it, answer `{ok:true, granted:false, pending}` (`pending` false once the request resolved, so a declining overlay knows whether to keep watching for a change of its word), then complete the roll call when no owner holds the lease and reports ≥ connected overlays. Verdict precedence: a report whose `reason` is not `no_match` (a tab that could serve later) → `{ok:false, error:'busy', state, reason}` at once; when every report is `no_match` the roll call stays open for `IMPECCABLE_AGENT_TARGET_RESOLVE_GRACE_MS` (default 3000) after each overlay's first such report (a late reporter extends the grace by the full window; a page whose element mounts late keeps re-checking while its decline answers `pending:true`, an eligible claim drops its stale report, and the overlay declines rather than posting a result when the element is gone after its claim), then answers the first report's `result` (e.g. `no_match` with `rawMatchCount`, `invalid_selector`); the timeout uses the same precedence when any report exists. `eligible === true` → drop this client's earlier report; `granted` when no owner, the same owner (renew), or the lease lapsed (`IMPECCABLE_AGENT_TARGET_CLAIM_LEASE_MS`, default 3000); answer `{ok:true, granted, pending:true}`. |
| anything else | | 404 `Not found` |
@@ -1575,7 +1575,7 @@ Journal line: `{"seq":N,"id":"…","type":"…","ts":"ISO","event":{…full even
Base snapshot: `{id, phase:'new', pageUrl:null, sourceFile:null, previewFile:null, previewMode:null, expectedVariants:0, arrivedVariants:0, visibleVariant:null, paramValues:{}, pendingEventSeq:null, pendingEvent:null, deliveryLease:null, checkpointRevision:0, browserCheckpointRevision:0, publicationCheckpointRevision:0, activeOwner:null, sourceMarkers:{}, fallbackMode:null, generationPhase:null, generationCompletedAt:null, generationTimings:{}, variantPlan:null, generationCanceled:false, generationCanceledAt:null, cancelReason:null, annotationArtifacts:[], mountedVariants:[], mountFailures:[], renderState:null, diagnostics:[], updatedAt:null}` (+ `detectorWaivers`, `message` when set).
Reducer per event type (`updatedAt = entry.ts`):
- `generate`: phase `generate_requested`; `origin` (the server stamps `"agent"` on a Go the generate verb fired; accept reads it back to decide the mechanical bake); pageUrl; expectedVariants=count; pendingEventSeq=seq; pendingEvent=event; variantPlan=null; mounted/mountFailures cleared, renderState null; screenshotPath → push `{type:'screenshot', path}` artifact.
- `generate`: phase `generate_requested`; `origin` (the server stamps `"agent"` on a Go the generate verb fired; the overlay restores its lane chrome from it); pageUrl; expectedVariants=count; pendingEventSeq=seq; pendingEvent=event; variantPlan=null; mounted/mountFailures cleared, renderState null; screenshotPath → push `{type:'screenshot', path}` artifact.
- `variant_plan` (unless canceled/fenced): variantPlan=plan. `detector_waivers`: append waivers.
- `agent_phase`: generationPhase=phase; `generationTimings[phase]={at, durationMs}`.
- `variants_ready`|`agent_done`: if canceled/fenced and not (agent_done carbonize in `accept_requested`) → diagnostic `late_generation_event_ignored`; else phase = `carbonize_required` if carbonize else `variants_ready`; generationCompletedAt; sourceFile=event.sourceFile??event.file; previewFile/previewMode; arrivedVariants = event.arrivedVariants ?? expected; clear pending; carbonize → diagnostic `carbonize_cleanup_required`; renderState derived (`mounted` if any mounted, `failed` if failures only, `pending` if completed, else null).
@@ -1686,7 +1686,7 @@ Order in `live-accept.mjs`: receipt check → find `impeccable-variants-start <i
- JSX: everything above wrapped in `<indent><div data-impeccable-carbonize="ID" style={{ display: "contents" }}>``</div>` with body indented 2 more, `<style …>{\`` / `\`}</style>`, `{/* … */}` comments, `style={{ display: 'contents' }}` on the variant div.
Result `{handled:true, file: rel, carbonize:boolean, todo?:'REQUIRED before next poll: carbonize cleanup in <file>. See reference/live.md "Required after accept".', bakeSkipped?}`. Discard: replace range with deindented original → `{handled:true, file, carbonize:false}`.
**Mechanical bake** (`bake.rs`): for a session whose snapshot carries `origin:'agent'` (a Go fired by `live-generate`) or on `--bake` (never on `--no-bake`; plain live sessions are untouched), a knob-free HTML/JSX accept is made permanent instead of leaving the carbonize block. Refused (falls back to the carbonize block, with `bakeSkipped:<reason>`) when: `--param-values` is non-empty; the accepted variant carries `data-impeccable-*` or `data-p-*` inside it; the preview CSS uses `var(--p-*)`, `data-p-*`, or `data-impeccable-params`; the variant's root is a component (`<PricingGrid>`, `<Card.Root>`: what it renders is unknown, and its `className` or `id` prop may never reach that element) or has neither an id nor a static class (`className={expr}`); a `:scope` cannot be rewritten (sibling combinators, `:scope` not at the front, nested `@scope`); the accepted variant declares no rule; or no destination stylesheet exists. The rewrite: the accepted `@scope ([data-impeccable-variant="N"])` block is flattened and every selector re-anchored on the root tag's selector (`#id`, else `tag.class.class`): `:scope > .x``.x`, `:scope .x``<anchor> .x`, `:scope:hover > .x``<anchor>:hover > .x`, bare `:scope``<anchor>`; Astro's `[data-impeccable-variant="N"] > .x` prefix the same way; nested `@media`/`@supports` inside the block keep their prelude; top-level `@keyframes`/`@font-face` are kept, other variants' blocks dropped. Destination: for `.jsx`/`.tsx` the `.css` file under the app root (skipping node_modules/.git/.impeccable/dist/build/coverage/framework caches, depth ≤ 6, `.min.css` and generated or git-ignored files excluded) with the most rules naming the anchor's id or classes, else the only `.css` file; for other files the page's own last `<style>` block when it has one, else the same search. The rules are **appended** under `/* impeccable generate <id>: accepted variant N */` (existing rules are never rewritten; a same-selector rule later in the cascade overrides declaration by declaration). The source is verified clean (`verifyAcceptedSource`) before anything is written; the stylesheet is written first, then the source with the variant unwrapped at the wrapper's indentation. Result `{handled:true, file, carbonize:false, baked:true, variant:'N', css:{file: rel|null (null = the page's own <style>), rules, anchor}, verify:{clean, findings}}`; the poll's completion for it is `complete`, so the session ends without `live-complete`. After accept with `--page-url`, buffered manual-edit ops whose original/new text appears as an exact text segment in the replaced original block are dropped from `pending-manual-edits.json`.
**Mechanical bake** (`bake.rs`): only on `--bake` (never by default and never on `--no-bake`; the generate lane's accept carbonizes exactly like plain live's, so the agent integrates the accepted variant per live.md), a knob-free HTML/JSX accept is made permanent instead of leaving the carbonize block. Refused (falls back to the carbonize block, with `bakeSkipped:<reason>`) when: `--param-values` is non-empty; the accepted variant carries `data-impeccable-*` or `data-p-*` inside it; the preview CSS uses `var(--p-*)`, `data-p-*`, or `data-impeccable-params`; the variant's root is a component (`<PricingGrid>`, `<Card.Root>`: what it renders is unknown, and its `className` or `id` prop may never reach that element) or has neither an id nor a static class (`className={expr}`); a `:scope` cannot be rewritten (sibling combinators, `:scope` not at the front, nested `@scope`); the accepted variant declares no rule; or no destination stylesheet exists. The rewrite: the accepted `@scope ([data-impeccable-variant="N"])` block is flattened and every selector re-anchored on the root tag's selector (`#id`, else `tag.class.class`): `:scope > .x``.x`, `:scope .x``<anchor> .x`, `:scope:hover > .x``<anchor>:hover > .x`, bare `:scope``<anchor>`; Astro's `[data-impeccable-variant="N"] > .x` prefix the same way; nested `@media`/`@supports` inside the block keep their prelude; top-level `@keyframes`/`@font-face` are kept, other variants' blocks dropped. Destination: for `.jsx`/`.tsx` the `.css` file under the app root (skipping node_modules/.git/.impeccable/dist/build/coverage/framework caches, depth ≤ 6, `.min.css` and generated or git-ignored files excluded) with the most rules naming the anchor's id or classes, else the only `.css` file; for other files the page's own last `<style>` block when it has one, else the same search. The rules are **appended** under `/* impeccable generate <id>: accepted variant N */` (existing rules are never rewritten; a same-selector rule later in the cascade overrides declaration by declaration). The source is verified clean (`verifyAcceptedSource`) before anything is written; the stylesheet is written first, then the source with the variant unwrapped at the wrapper's indentation. Result `{handled:true, file, carbonize:false, baked:true, variant:'N', css:{file: rel|null (null = the page's own <style>), rules, anchor}, verify:{clean, findings}}`; the poll's completion for it is `complete`, so the session ends without `live-complete`. After accept with `--page-url`, buffered manual-edit ops whose original/new text appears as an exact text segment in the replaced original block are dropped from `pending-manual-edits.json`.
Receipt: on any `handled!==false` result write `accept-receipts/<id>.json` = `{id, operation:'accept'|'discard', variantId:'N'|null, result, completedAt}` (tmp+rename). Re-run with same op/variant → prior `result` + `{handled:true, alreadyApplied:true}`; different → `{handled:false, mode:'error', error:'accept_receipt_conflict', priorOperation, priorVariantId}`.
@@ -1804,7 +1804,7 @@ Conventions: every script's "run directly" guard is `process.argv[1]` ending wit
- **Invoked from**: `skill/reference/generate.md` (the `generate` command), as the lane's one start command after the agent opened the page in its harness's own browser: `impeccable live-generate --target src/App.jsx --dev-url http://127.0.0.1:5173/ --selector ".pricing-grid" --action bolder --count 3 --boot --wait-for-browser 60000` (`--open` only on a harness with no browser tool; without `--dev-url` and without a wait, the verdict is `browser_needed` and the agent comes back with the page open).
- **Args**: `--selector <css>` (required), `--text <snippet>`, `--index <n>` (1-based), `--action <name>` (default `impeccable`), `--count <n>` (default 3, 1-8), `--prompt <text>`, `--dry-run`, `--wait-for-browser <ms>`, `--no-live-bar` (body `hideLiveBar:true`: the helper sets its lifetime-wide `hideLiveBar` preference, broadcasts `{type:'live_bar', hidden:true}` to every connected overlay before the target goes out, and answers `hideLiveBar:true` on every later `connected` frame; the overlay hides its global bar accordingly and skips its "No PRODUCT.md found" connect notice, the variant controls still show, and only the helper stopping ends it), `--target <path>` (consumed by `enterLiveRoot`, and the boot's --target under `--boot`), `--boot` (run `live --allow-missing-context --dev-url --no-live-bar [--target]` in-process from the caller's cwd first, reusing a running helper; implies `hideLiveBar:true` on the target; the boot's `devUrl, pageFiles, projectRoot, targetPath, liveBarHidden, contextMissing, contextNote, hasProduct, product, productPath, hasDesign, design, designPath, hasSurfaceBrief, surfaceBrief, surfaceBriefPath` ride along as `boot`; a refusing boot is printed as its own payload plus `ok:false`, `bootError:<its error>` and `_instructions`, exit 1; an unparseable boot → `boot_failed` (+`exitCode`, `detail`)), `--open` (ignored, with `openIgnored:'harness browser'` on the verdict and an `_instructions` prefix saying so, when the provider id is `cursor` or `claude-code` and neither `IMPECCABLE_BROWSER` nor the config's `browser` names a browser: a harness with its own browser never gets a second window from this verb, and the generic `BROWSER` variable is not that choice; otherwise, when `/status` reports no connected client: the dev URL is the boot's `devUrl` or a fresh `dev_url::probe`; none → `no_dev_server`, exit 1; else the URL is opened without waiting for the browser (`IMPECCABLE_BROWSER`, then `browser` in `.impeccable/config.local.json` / `.impeccable/config.json` at the app root, then `BROWSER`, then `open` / `xdg-open` / `cmd /c start`; a value with a path separator runs as a program with the URL as its argument, on macOS any other value is `open -a <name>`), recorded as `opened:{url, via}`, and `--wait-for-browser` defaults to 60000; a launch failure → `browser_open_failed` (+`url`, `detail`)), `--allow-missing-context` (accepted and ignored), `--dev-url [<url>]` (bare: ignored, the boot's own flag; with a value: the dev server the caller already knows, put first in the boot's probe list (`IMPECCABLE_DEV_URL_CANDIDATES` still wins when set) and in this verb's own probe, and reported as `devUrl` with `devUrlVerified:false` when no probe confirmed the tag on it), `--help`. A flag without a value → stdout `{"ok":false,"error":"missing_flag_value","flag":"--x"}`, exit 1.
- **Env**: `IMPECCABLE_SELF` (how the boot and poll verbs are spelled in `_instructions`).
- **Behavior**: `enterLiveRoot`; local verdicts first, each pretty-printed JSON on stdout with `_instructions`, exit 1: `selector_required`, `invalid_action` (+`action`, `validActions`), `invalid_count` (+`count`), `invalid_index` (+`index`), `invalid_wait` (+`wait`); no `server.json` (or one without port/token) → `server_not_running`. With `--wait-for-browser`, `GET /status` once a second until `connectedClients > 0` or the budget ends (`no_browser_connected` + `waitedMs`); while it waits, the dev URL it knows (the one it opened, else the boot's `devUrl`, else the probe or the caller's hint) gets a TCP connect every third tick, and two misses in a row end the wait early with `dev_server_gone` (+`devUrl`, `waitedMs`, `harness`) whose `_instructions` name the harness's way to start the dev script and rerun with `--dev-url`; an unanswered `/status``server_unreachable`. Then `POST /agent-target` with `{token, selector, action, count, text?, index?, prompt?, dryRun?}` under a 20 s client cap: a transport timeout → `request_timeout` (+`detail`, browser_timeout instructions), any other transport failure → `server_unreachable` (+`detail`); a non-2xx answer → `{ok:false, error:<body.error or http_<status>>, ...body}`; an unparseable body → `bad_server_response` (+`status`). Before the target goes out, with no page connected (`/status` `connectedClients` 0), no `--open`, no `--wait-for-browser`, and no `--dry-run`: `browser_needed` (`devUrl` from the boot's probe, else a probe led by the hint, else the hint unverified; `devUrlVerified`; `harness`, the provider id) or `no_dev_server` when no URL is known, exit 1. Its `_instructions` name the harness's own browser and never a second one: `cursor``browser_navigate` (reuse the tab on that origin); `claude-code` → the Browser pane (`navigate` the tab already on that origin, `tabs_context`, `preview_start` with the URL when the pane is closed); `codex` → rerun with `--open` or give the user the URL; others → the harness browser tool, else `--open` or the user; then rerun with `--wait-for-browser 60000`. `no_dev_server` names where this harness starts a server (`claude-code`: `preview_start` or the dev script; others: the dev script in a background terminal) and asks for `--dev-url <url>` on the rerun. Every verdict from the boot on carries `harness`. A started session (`ok:true`, not a dry run) then collects its own generate event: `GET /poll?types=generate&id=<sessionId>` in ≤5 s slices for up to 20 s, leased exactly as a poll leases it (the preflight scaffold runs on lease), printed as `event` with locally generated `_instructions` (the fast path), or `event:null` when it did not arrive. A 2xx answer is printed as received plus `boot`/`opened` when those ran, plus `_instructions` for `ok` (dry run; started session with `event`, pointing at the edit and at `live-poll --reply <id> done --file <path> --then-poll`; started session without it, pointing at `live-poll` first), `no_dev_server`, `browser_open_failed`, `no_browser_connected` (a variant when `opened` is present), `browser_timeout`, `invalid_selector`, `no_match` (wording depends on `rawMatchCount`), `ambiguous`, `index_out_of_range`, `busy`, `go_failed`, `server_stopping`; exit 0 when `ok:true`, else 1. `_instructions` are regenerated locally from the verdict, never taken from the wire.
- **Behavior**: `enterLiveRoot`; local verdicts first, each pretty-printed JSON on stdout with `_instructions`, exit 1: `selector_required`, `invalid_action` (+`action`, `validActions`), `invalid_count` (+`count`), `invalid_index` (+`index`), `invalid_wait` (+`wait`); no `server.json` (or one without port/token) → `server_not_running`. With `--wait-for-browser`, `GET /status` once a second until `connectedClients > 0` or the budget ends (`no_browser_connected` + `waitedMs`); while it waits, the dev URL it knows (the one it opened, else the boot's `devUrl`, else the probe or the caller's hint) gets a TCP connect every third tick, and two misses in a row end the wait early with `dev_server_gone` (+`devUrl`, `waitedMs`, `harness`) whose `_instructions` name the harness's way to start the dev script and rerun with `--dev-url`; an unanswered `/status``server_unreachable`. Then `POST /agent-target` with `{token, selector, action, count, text?, index?, prompt?, dryRun?}` under a 20 s client cap: a transport timeout → `request_timeout` (+`detail`, browser_timeout instructions), any other transport failure → `server_unreachable` (+`detail`); a non-2xx answer → `{ok:false, error:<body.error or http_<status>>, ...body}`; an unparseable body → `bad_server_response` (+`status`). Before the target goes out, with no page connected (`/status` `connectedClients` 0), no `--open`, no `--wait-for-browser`, and no `--dry-run`: `browser_needed` (`devUrl` from the boot's probe, else a probe led by the hint, else the hint unverified; `devUrlVerified`; `harness`, the provider id) or `no_dev_server` when no URL is known, exit 1. Its `_instructions` name the harness's own browser and never a second one: `cursor``browser_navigate` (reuse the tab on that origin); `claude-code` → the Browser pane (`navigate` the tab already on that origin, `tabs_context`, `preview_start` with the URL when the pane is closed); `codex` → rerun with `--open` or give the user the URL; others → the harness browser tool, else `--open` or the user; then rerun with `--wait-for-browser 60000`. `no_dev_server` names where this harness starts a server (`claude-code`: `preview_start` or the dev script; others: the dev script in a background terminal) and asks for `--dev-url <url>` on the rerun. Every verdict from the boot on carries `harness`. A started session (`ok:true`, not a dry run) then collects its own generate event: `GET /poll?types=generate&id=<sessionId>` in ≤5 s slices for up to 20 s, leased exactly as a poll leases it (the preflight scaffold runs on lease), printed as `event` with locally generated `_instructions` (the same planning steps `live-poll` would print for a user's Go), or `event:null` when it did not arrive. A 2xx answer is printed as received plus `boot`/`opened` when those ran, plus `_instructions` for `ok` (dry run; started session with `event`, pointing at the edit and at `live-poll --reply <id> done --file <path> --then-poll`; started session without it, pointing at `live-poll` first), `no_dev_server`, `browser_open_failed`, `no_browser_connected` (a variant when `opened` is present), `browser_timeout`, `invalid_selector`, `no_match` (wording depends on `rawMatchCount`), `ambiguous`, `index_out_of_range`, `busy`, `go_failed`, `server_stopping`; exit 0 when `ok:true`, else 1. `_instructions` are regenerated locally from the verdict, never taken from the wire.
- **Tests**: `tests/oracle/cases/live-generate.mjs` (local verdicts, no-browser), `tests/live-agent-target.test.mjs` (protocol matrix against the binary), `crates/cli/tests/agent_target.rs`, `tests/live-e2e.test.mjs` (`agentTargetScenario`).
#### `live-commit-manual-edits.mjs` -> `impeccable commit-manual-edits`