Restore polling as the primary Live architecture

Default Codex back to one-shot foreground polling, delegate generation to the existing low-effort agent, and keep the app-server worker available only through an explicit experimental opt-in. Preserve progressive publication and the shared safety and framework optimizations.

Prepared with Codex assistance under maintainer direction.
This commit is contained in:
Paul Bakaus
2026-07-15 16:16:08 -07:00
parent ead6ddabe5
commit 0ac1ca6867
58 changed files with 424 additions and 361 deletions
+18 -19
View File
@@ -12,25 +12,25 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .agents/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
- **Default (Claude Code, Cursor, and other harnesses):** keep the established atomic single-edit delivery unless that harness has independently demonstrated that progressive tool calls are faster and reliable. This avoids trading model latency for extra tool-call latency on harnesses with different streaming behavior.
- **Codex progressive override:** deliver progressively through `live-publish.mjs`, never by editing project source directly. Publish variant 1 as soon as it is complete, then publish variants 2 through N and all parameter CSS/manifests together from the second design turn. The browser makes every arrived variant immediately reviewable and acceptable; Accept/Discard durably cancel unfinished revisions.
- **Codex progressive override:** deliver progressively through `live-publish.mjs`, never by editing project source directly. Publish variant 1 as soon as it is complete, then publish each additional validated variant (or the largest ready prefix) without waiting for later siblings. Attach parameter CSS/manifests only with the complete set. The browser makes every arrived variant immediately reviewable and acceptable; Accept/Discard durably cancel unfinished revisions.
Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodies. Spend tokens on tools and edits; on failure, one or two short sentences.
@@ -42,9 +42,9 @@ node .agents/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -97,26 +97,27 @@ node .agents/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .agents/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .agents/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .agents/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -375,8 +376,8 @@ node .agents/skills/impeccable/scripts/live-publish.mjs --id EVENT_ID --epoch EP
```
`{ok:false,error:"stale_generation_epoch"}` means the user already accepted or discarded. Stop immediately, do not touch source, and post the generation reply as canceled/error.
4. In the same persistent thread, generate variants 2 through `EVENT_COUNT` together from the stored plan plus parameter wiring/manifests for every variant. Run `--prepare` again so the revision starts from the immutable published prefix, add the complete remaining set and parameters without changing variant 1 or any default appearance, then publish with `--arrived EVENT_COUNT --kind params`. On component-preview paths, preserve every already-published `vN.svelte` / `vN.vue` byte-for-byte; publication rejects a revision that silently changes a variant the user may already be reviewing.
5. A params-only turn is recovery-only: use it when durable state says every variant arrived but `paramsPublished` is still false after an interrupted publication.
4. Continue variants 2 through `EVENT_COUNT` from the stored plan. Whenever another direction validates, run `--prepare` again so the revision starts from the immutable published prefix, add the largest ready prefix without changing any published variant or default appearance, and publish it immediately. Attach parameter CSS/manifests only when the complete set is ready, using `--kind params`. On component-preview paths, preserve every already-published `vN.svelte` / `vN.vue` byte-for-byte; publication rejects a revision that silently changes a variant the user may already be reviewing.
5. A params-only pass is recovery-only: use it when durable state says every variant arrived but `paramsPublished` is still false after an interrupted publication.
6. Verify the published preview parses, then `--reply done`. A late reply is diagnostic only after Accept/Discard and cannot move the durable session backward.
Use the `cssAuthoring` object returned by `live-wrap.mjs` to author the temporary preview CSS. The style opening tag shown below is the common case; replace it with `cssAuthoring.styleTag` when the tool returns a different one. The variant markup shape is otherwise stable:
@@ -552,9 +553,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -562,9 +561,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)—not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .claude/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .claude/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .claude/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .claude/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .claude/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .claude/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)—not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .cursor/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .cursor/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .cursor/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .cursor/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .cursor/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .cursor/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .gemini/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .gemini/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .gemini/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .gemini/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .gemini/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .gemini/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .github/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .github/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .github/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .github/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .github/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .github/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .kiro/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .kiro/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .kiro/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .kiro/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .kiro/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .kiro/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .opencode/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .opencode/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .opencode/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .opencode/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .opencode/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .opencode/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .pi/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .pi/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .pi/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .pi/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .pi/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .pi/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
+3 -2
View File
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .qoder/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .qoder/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .qoder/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .qoder/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .qoder/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .qoder/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .rovodev/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .rovodev/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .rovodev/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .rovodev/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .rovodev/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .rovodev/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .trae-cn/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .trae-cn/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .trae-cn/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .trae-cn/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .trae-cn/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .trae-cn/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .trae/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .trae/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .trae/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .trae/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .trae/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .trae/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+15 -16
View File
@@ -10,19 +10,19 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node .claude/skills/impeccable/scripts/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
@@ -38,9 +38,9 @@ node .claude/skills/impeccable/scripts/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -93,26 +93,27 @@ node .claude/skills/impeccable/scripts/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node .claude/skills/impeccable/scripts/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node .claude/skills/impeccable/scripts/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node .claude/skills/impeccable/scripts/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -523,9 +524,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -533,9 +532,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+18 -19
View File
@@ -14,26 +14,26 @@ Execute in order. No step skipped, no step reordered.
1. `live.mjs`: boot. If the request names or implies a file, route, or app inside a monorepo, infer the concrete path and run `node {{scripts_path}}/live.mjs --target <path>` instead; then run the rest of this live session from the returned `projectRoot`.
2. Open the app URL that serves `pageFile` (infer from `package.json`, docs, terminal output, or an open tab). Never use `serverPort`; it's the helper, not the app. **Cursor:** `browser_navigate` to that URL before polling; do not skip. **Other harnesses:** use the available browser tool; if the URL is uncertain, ask the user once.
3. Poll loop with the default long timeout (600000 ms). Portable harnesses run `live-poll.mjs` again immediately after every event or `--reply`. Codex with the dedicated worker keeps the returned `--stream` control command alive instead. Never pass a short `--timeout=`.
3. Poll loop with the default long timeout (600000 ms). Run `live-poll.mjs` again immediately after every event or `--reply`; Codex runs this one-shot poll in the foreground. Never pass a short `--timeout=`.
The global bar **Impeccable mark** dims and shows a pulsing amber dot when no agent is long-polling `/poll`. Hover the mark for the hint; restart `live-poll.mjs` to reconnect.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; plan three distinct directions; deliver variants using the harness policy below; `--reply done`; poll again.
4. On `generate`: reuse `event.scaffold` when present; read the screenshot if present; load the action's reference; deliver variants using the harness policy below; `--reply done`; poll again. In Codex, delegate the complete event to `impeccable_live_generator` and resume the foreground poll immediately; the generator owns publication and the reply.
5. On `steer`: read the message and `pageUrl`; do the work (page edits, navigation help, or a short reply in the `--reply` message); `--reply steer_done`; poll again. No pickup ack. The Steer bar unlocks when `steer_done` arrives over SSE.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground control task runs `live-complete.mjs --id EVENT_ID`; the Codex control stream waits for that event reply without exiting, while synchronous harnesses finish cleanup before polling again.
6. On `accept` / `discard`: the poll script runs `live-accept.mjs`, acknowledges the delivered event, and prints `_completionAck`. Plain accepts/discards are terminal immediately. Carbonize accepts remain recoverable until the foreground task runs `live-complete.mjs --id EVENT_ID`; finish that cleanup before polling again.
7. If interrupted, run `live-status.mjs` or `live-resume.mjs` before guessing. The durable journal replays unacknowledged work after helper restart.
8. On `exit`: run the cleanup at the bottom.
Harness policy:
- **Claude Code**: run the poll as a **background task** (no short timeout). The harness notifies you when it completes, so the main conversation stays free. Do not block the shell.
- **Cursor**: run **one-shot** poll in a **background terminal** with notify on `"type":"(steer|generate|accept|discard|exit)"`. After each event the poll exits; handle it, `--reply`, then start `live-poll.mjs` again. Do **not** use `--stream` on Cursor: incremental stdout notify is slower in practice than exit-based notify (~5s vs sub-second in testing).
- **Codex**: `live.mjs` starts a dedicated app-server generation lane by default and returns its exact persistent foreground control command. Run that `--stream` command once in a yielded foreground exec session; do not suffix it with `&`, restart it after each event, or launch an overlapping poll. The yielded shell remains alive while the model handles Steer, manual Apply, or carbonize through separate tool calls; after the model posts `--reply`, the same stream resumes polling automatically. Keep the task open until Live exits. The stream dynamically adds generation events only if the dedicated worker fails or exits. When the worker is explicitly disabled or unavailable, preserve the portable one-shot foreground path and delegate to the low-effort `impeccable_live_generator` agent with a compact handoff. Do not paste this full reference into the handoff.
- **Codex**: run the default one-shot poll in a **yielded foreground exec session**. Do not suffix it with `&`, use `--stream`, or leave Live without an active foreground poll. When `generate` arrives, delegate to the low-effort `impeccable_live_generator` agent with a compact handoff, then immediately start the next foreground poll while that agent publishes and replies. Do not paste this full reference into the handoff. Handle Steer, Accept/Discard, manual Apply, carbonize, and Exit in the main task; after each handler/reply, restart the foreground poll.
- **Other harnesses**: one-shot foreground unless you know stdout reliably returns to this session when a shell exits.
Generation delivery policy:
- **Default (Claude Code, Cursor, and other harnesses):** keep the established atomic single-edit delivery unless that harness has independently demonstrated that progressive tool calls are faster and reliable. This avoids trading model latency for extra tool-call latency on harnesses with different streaming behavior.
<codex>
- **Codex progressive override:** deliver progressively through `live-publish.mjs`, never by editing project source directly. Publish variant 1 as soon as it is complete, then publish variants 2 through N and all parameter CSS/manifests together from the second design turn. The browser makes every arrived variant immediately reviewable and acceptable; Accept/Discard durably cancel unfinished revisions.
- **Codex progressive override:** deliver progressively through `live-publish.mjs`, never by editing project source directly. Publish variant 1 as soon as it is complete, then publish each additional validated variant (or the largest ready prefix) without waiting for later siblings. Attach parameter CSS/manifests only with the complete set. The browser makes every arrived variant immediately reviewable and acceptable; Accept/Discard durably cancel unfinished revisions.
</codex>
Chat is overhead. No recap, no tutorial output, no pasting PRODUCT / DESIGN bodies. Spend tokens on tools and edits; on failure, one or two short sentences.
@@ -46,9 +46,9 @@ node {{scripts_path}}/live.mjs
Output JSON: `{ ok, serverPort, serverToken, pageFiles, hasProduct, product, productPath, hasDesign, design, designPath }`. `pageFiles` is the list of HTML entries the live script was injected into. Keep PRODUCT.md and DESIGN.md in mind for variant generation; **DESIGN.md wins on visual decisions; PRODUCT.md wins on strategic/voice decisions.** When DESIGN.md is missing, identity is **not** absent; extract it from CSS variables, computed styles, and sibling components on the page (see Step 4 Phase A). Identity preservation is the default; departure from existing identity requires an explicit trigger from PRODUCT.md anti-references or the user's freeform prompt.
If output includes `codexWorker.enabled: true`, run the returned `codexWorker.foregroundPoll` command once and keep its yielded exec session alive. The dedicated lane owns `generate,accept,discard,prefetch`; the foreground owns `steer,manual_edit_apply,carbonize_cleanup,exit`. The fallback flag restores generation to the foreground only when the worker's owned process record is failed or unreachable. When a control event is printed, handle it through separate tool calls and post its reply; the stream waits for that acknowledgement and then resumes automatically. The browser reports foreground availability while this stream is polling or waiting for its leased control event acknowledgement. Do not start another poll, and do not end the task with a final response until Live exits.
Normally `codexWorker.enabled` is `false`; run the default unfiltered `live-poll.mjs` command in the foreground. If an explicitly opted-in run returns `codexWorker.enabled: true`, follow the experimental worker section below instead.
If output includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live is using foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session. The browser mark carries a static status dot and explains that installing Codex CLI, running `codex login`, and restarting Live enables background variants.
If an explicitly opted-in run includes `codexWorker.error: "codex_cli_unavailable"`, tell the user once that Live fell back to foreground generation, then run the returned unfiltered `codexWorker.foregroundPoll`. Do not retry or install anything during the session.
`serverPort` and `serverToken` belong to the small **Impeccable live helper** HTTP server (serves `/live.js`, SSE, and `/poll`). That port is **not** your dev server and is usually not the URL you open to view the app. The browser page is whatever origin serves one of the `pageFiles` entries (Vite / Next / Bun / tunnel / LAN hostname).
@@ -101,26 +101,27 @@ node {{scripts_path}}/live-complete.mjs --id SESSION_ID
Server restart rule: start `live-server.mjs` again, then poll. Startup requeues unacknowledged pending events from the journal, so do not ask the user to click Go again unless `live-resume.mjs` says no active session exists.
### Dedicated Codex worker
### Experimental dedicated Codex worker
Codex uses a Live-owned persistent app-server supervisor instead of using the desktop task as the poll supervisor:
The app-server supervisor is retained for controlled experiments, but it is not the primary Codex path. Enable it explicitly for a run:
```bash
node {{scripts_path}}/live.mjs
IMPECCABLE_LIVE_CODEX_WORKER=1 node {{scripts_path}}/live.mjs
```
Activation is process-local: the worker is enabled by default only when the process carries a Codex runtime signal, so a committed setting cannot switch another harness onto Codex. Set `IMPECCABLE_LIVE_CODEX_WORKER=0` to force the portable foreground path. Project config may tune delivery or select the explicit `fast` profile without enabling the worker in another harness:
Activation remains process-local. The worker is off by default, including in Codex. `IMPECCABLE_LIVE_CODEX_WORKER=1` is the direct opt-in; alternatively, Codex may opt in through `experimentalCodexWorker.enabled`. A committed setting can never switch a non-Codex harness onto this path:
```json
{
"experimentalCodexWorker": {
"enabled": true,
"profile": "quality",
"delivery": "progressive"
}
}
```
The app-server worker is **default-on in Codex and Codex-only**. Claude, Gemini, Cursor, and every other harness keep the portable foreground/atomic behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback with setup guidance instead of a misleading prewarm state. Otherwise Live records the worker as `starting` and returns immediately, so app-server initialization overlaps page/dev-server startup. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
When explicitly enabled, the worker remains Codex-only. Claude, Gemini, Cursor, and every other harness keep their portable polling behavior. Before detaching anything, Live resolves the configured Codex executable using the same explicit-path/PATH rules as Node spawn. A missing CLI becomes an immediate, durable foreground fallback. Otherwise Live records the worker as `starting` and returns immediately. Run only the returned foreground control poll. It checks the owned worker process every two seconds and safely restores generation/accept/discard leasing if startup, authentication, model selection, or the worker process fails. Dedicated-worker leases expire after 15 seconds, so a hard process loss cannot strand browser work behind the portable ten-minute lease.
```bash
node {{scripts_path}}/live-poll.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit --codex-worker-fallback
@@ -380,8 +381,8 @@ node .agents/skills/impeccable/scripts/live-publish.mjs --id EVENT_ID --epoch EP
```
`{ok:false,error:"stale_generation_epoch"}` means the user already accepted or discarded. Stop immediately, do not touch source, and post the generation reply as canceled/error.
4. In the same persistent thread, generate variants 2 through `EVENT_COUNT` together from the stored plan plus parameter wiring/manifests for every variant. Run `--prepare` again so the revision starts from the immutable published prefix, add the complete remaining set and parameters without changing variant 1 or any default appearance, then publish with `--arrived EVENT_COUNT --kind params`. On component-preview paths, preserve every already-published `vN.svelte` / `vN.vue` byte-for-byte; publication rejects a revision that silently changes a variant the user may already be reviewing.
5. A params-only turn is recovery-only: use it when durable state says every variant arrived but `paramsPublished` is still false after an interrupted publication.
4. Continue variants 2 through `EVENT_COUNT` from the stored plan. Whenever another direction validates, run `--prepare` again so the revision starts from the immutable published prefix, add the largest ready prefix without changing any published variant or default appearance, and publish it immediately. Attach parameter CSS/manifests only when the complete set is ready, using `--kind params`. On component-preview paths, preserve every already-published `vN.svelte` / `vN.vue` byte-for-byte; publication rejects a revision that silently changes a variant the user may already be reviewing.
5. A params-only pass is recovery-only: use it when durable state says every variant arrived but `paramsPublished` is still false after an interrupted publication.
6. Verify the published preview parses, then `--reply done`. A late reply is diagnostic only after Accept/Discard and cannot move the durable session backward.
</codex>
@@ -558,9 +559,7 @@ Event: `{id, variantId, _acceptResult, _completionAck}`. The poll script already
When `_acceptResult.carbonize === true`, the accepted variant was stitched into source with helper markers and inline CSS so the browser can render it immediately with no visual gap. That stitch-in is **temporary**. The agent must rewrite it into permanent form before doing anything else. Skipping this leaves dead `@scope` rules for unaccepted variants, a pointless `data-impeccable-variant` wrapper, and `impeccable-carbonize-start/end` comment noise in the source file; all of which accumulate across sessions.
**Codex:** the persistent foreground control stream prints the cleanup event and then waits for its acknowledgement; this does not block the model. Leave that yielded exec session running, perform these five steps through separate tool calls in the main task, run `live-complete.mjs --id SESSION_ID`, and post the event reply. The same stream resumes polling automatically after the acknowledgement. A later Generate cannot be leased by this waiting control stream; the source lock, generation epoch, and expected-source hash remain the final safety gates.
**Other harnesses:** unless an equivalent independently supervised cleanup worker is proven, do these five steps synchronously before the next poll.
Do these five steps synchronously before the next poll. The source lock, generation epoch, and expected-source hash remain the final safety gates against a generator finishing concurrently with Accept.
1. **Locate the carbonize block** in the source file (`_acceptResult.file`). It's bracketed by `<!-- impeccable-carbonize-start SESSION_ID -->` and `<!-- impeccable-carbonize-end SESSION_ID -->` and contains a `<style data-impeccable-css="SESSION_ID">` element. If the variant declared parameters, an `<!-- impeccable-param-values SESSION_ID: {...} -->` comment sits alongside the style tag with the user's chosen values; read it first; it drives steps 3 and 4 below.
2. **Move the CSS rules** into the project's real stylesheet. Which stylesheet depends on the project (e.g. `site/styles/workflow.css` for an Astro project, or the component's co-located CSS file for a Vite/Next project; pick whichever already owns styling for the surrounding element).
@@ -568,9 +567,9 @@ When `_acceptResult.carbonize === true`, the accepted variant was stitched into
4. **Unwrap the accepted content.** Delete the inner `<div data-impeccable-variant="N" style="display: contents">` that wraps it. On JSX/TSX, also delete the outer `<div data-impeccable-carbonize="SESSION_ID" style={{ display: 'contents' }}>` wrapper if present (accept adds it so ternary/`return` slots keep a single root). Drop `data-impeccable-params` and any `data-p-*` attributes; those are live-mode plumbing, not source.
5. **Delete the inline `<style>` block, the `<!-- impeccable-param-values -->` comment if present, and both `<!-- impeccable-carbonize-start/end -->` markers.** Also drop any `@scope` rules for variants other than the accepted one; those are dead code now.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. The Codex supervisor keeps polling throughout; synchronous harnesses poll again only after that verification.
After the file is clean, the cleanup owner runs `live-complete.mjs --id SESSION_ID` and verifies `phase: "completed"`. Poll again only after that verification.
With the dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. Do not restart the control poll; the existing stream resumes after this reply.
With the experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event: `{id, sessionId, file, variantId, acceptResult}`. Perform the same five steps above for `sessionId`, run `live-complete.mjs --id SESSION_ID`, then acknowledge the control event with `live-poll.mjs --reply EVENT_ID complete --file FILE`. The experimental stream resumes after this reply.
## Handle `discard`
+3 -2
View File
@@ -359,8 +359,9 @@ Options:
--help Show this help message
Harness note:
Default one-shot mode is the portable contract for Claude Code, Cursor, and foreground fallback.
Codex uses --stream only for the dedicated worker's narrow foreground control lane.
Default one-shot mode is the primary contract, including Codex foreground polling.
Claude Code may run it as a background task; Cursor uses a background terminal with exit notification.
--stream is retained for explicitly enabled experimental worker control lanes.
Do not use --stream on Cursor.`);
process.exit(0);
}
+4 -4
View File
@@ -10,7 +10,7 @@
*
* After this, the agent's only remaining steps are:
* - Open the project's live dev/preview URL in the browser (optional, if browser automation exists)not `serverPort`; that port is the Impeccable helper for /live.js and /poll
* - Enter the poll loop: `node live-poll.mjs`
* - Enter the harness-native poll loop: `node live-poll.mjs`
*
* Usage:
* node live.mjs # Prepare everything, print JSON, exit
@@ -41,7 +41,7 @@ Prepare everything for live variant mode in a single command:
- Starts (or reuses) the live server in the background
- Injects the browser script tag
- Reads PRODUCT.md / DESIGN.md for project context
- Starts the dedicated app-server worker by default in Codex
- Keeps the experimental Codex app-server worker off unless explicitly enabled
- In monorepos, choose a child app first; --target <path> is the fallback/manual path
On success, prints a JSON blob with:
@@ -131,8 +131,8 @@ The agent should then:
const resolvedFiles = resolveFiles(activeCwd, checkResult.config);
const drift = scanForDrift(activeCwd, resolvedFiles, checkResult.config);
// Codex-only and default-on in Codex. A failed app-server startup never takes
// ownership of the poll queue; the foreground portable path remains active.
// Codex-only and explicitly opt-in. The foreground portable path is the
// default, and a failed app-server startup never takes ownership of its queue.
const codexWorker = ensureCodexWorker(activeCwd, checkResult.config);
// 5. Emit everything the agent needs
+7 -3
View File
@@ -152,9 +152,13 @@ function codexSourceDeltaOutputSchema(phase, requirePlan, expectedVariants) {
export function resolveCodexWorkerConfig({ env = process.env, liveConfig = {} } = {}) {
const configured = liveConfig.experimentalCodexWorker || liveConfig.codexWorker || {};
const envEnabled = parseBoolean(env.IMPECCABLE_LIVE_CODEX_WORKER);
// Activation remains process-local. Codex gets the worker by default, while
// committed project settings can never switch another harness onto Codex.
const enabled = envEnabled == null ? isCodexRuntime(env) : envEnabled;
const configuredEnabled = parseBoolean(configured.enabled);
// The app-server lane is experimental and opt-in. A committed project
// setting can enable it only inside Codex; it can never switch another
// harness onto a Codex-specific runtime path.
const enabled = envEnabled == null
? isCodexRuntime(env) && configuredEnabled === true
: envEnabled;
const profile = nonEmpty(env.IMPECCABLE_LIVE_CODEX_PROFILE)
|| nonEmpty(configured.profile)
|| 'quality';
+6 -2
View File
@@ -22,7 +22,7 @@ import {
} from '../skill/scripts/live/codex-worker.mjs';
describe('Codex Live worker configuration', () => {
it('defaults on only inside Codex and preserves explicit overrides', () => {
it('defaults off everywhere and preserves explicit Codex-only opt-ins', () => {
assert.deepEqual(resolveCodexWorkerConfig({ env: {}, liveConfig: {} }), {
enabled: false,
model: null,
@@ -44,7 +44,11 @@ describe('Codex Live worker configuration', () => {
env: {},
liveConfig: { experimentalCodexWorker: { enabled: true, delivery: 'atomic' } },
}).enabled, false, 'committed config cannot activate Codex in another harness');
assert.equal(resolveCodexWorkerConfig({ env: { CODEX_THREAD_ID: 'thread-1' } }).enabled, true);
assert.equal(resolveCodexWorkerConfig({ env: { CODEX_THREAD_ID: 'thread-1' } }).enabled, false);
assert.equal(resolveCodexWorkerConfig({
env: { CODEX_THREAD_ID: 'thread-1' },
liveConfig: { experimentalCodexWorker: { enabled: true } },
}).enabled, true);
assert.equal(resolveCodexWorkerConfig({
env: { CODEX_THREAD_ID: 'thread-1', IMPECCABLE_LIVE_CODEX_PROFILE: 'fast' },
}).effort, 'low');
+6 -3
View File
@@ -41,16 +41,19 @@ describe('live reference authoring contract', () => {
const openingContract = liveMd.split('\n').slice(0, 60).join('\n');
assert.match(liveMd, /1\. `live\.mjs`: boot\./);
assert.match(liveMd, /3\. Poll loop with the default long timeout \(600000 ms\)\. Portable harnesses run `live-poll\.mjs` again immediately.*Codex with the dedicated worker keeps the returned `--stream` control command alive instead\./);
assert.match(liveMd, /3\. Poll loop with the default long timeout \(600000 ms\)\. Run `live-poll\.mjs` again immediately.*Codex runs this one-shot poll in the foreground\./);
assert.match(openingContract, /## Poll loop/);
assert.match(openingContract, /No step skipped, no step reordered\./);
assert.doesNotMatch(liveMd, /live-copy-edits\.md/);
assert.doesNotMatch(liveMd, /IMPECCABLE_LIVE_COPY_AGENT|mock/);
assert.match(liveMd, /"manual_edit_apply" → Handle Manual Edit Apply/);
assert.match(liveMd, /## Handle `manual_edit_apply`/);
assert.match(liveMd, /live-poll\.mjs --stream --types=steer,manual_edit_apply,carbonize_cleanup,exit/);
assert.match(openingContract, /Codex.*one-shot poll in a \*\*yielded foreground exec session\*\*/);
assert.doesNotMatch(openingContract, /dedicated app-server generation lane by default/);
assert.match(liveMd, /Experimental dedicated Codex worker/);
assert.match(liveMd, /IMPECCABLE_LIVE_CODEX_WORKER=1 node/);
assert.match(liveMd, /narrow, reasoned per-candidate waivers/);
assert.match(liveMd, /Accept emits a foreground `carbonize_cleanup` control event/);
assert.match(liveMd, /experimental dedicated worker, Accept emits a foreground `carbonize_cleanup` control event/i);
assert.ok(
liveMd.indexOf('## Handle `manual_edit_apply`') > liveMd.indexOf('## Handle `prefetch`'),
'manual_edit_apply handler section must sit after prefetch in the dispatch order',