feat(lanes): auto-advance a codex lane's stage from its rollout detection
A codex session emits no hooks and rarely calls `ccam stage` on its own, so its card sat at `idle` forever while the amber "detected" badge was the only thing that ever moved. `codex-ingest.js` now advances the declared `stage` from its own written detection — the one place in CCAM where an inference moves the blue `current` ring, and only for `codex` lanes. It stays honest: gated on `recordDetection` having written (so forward-only, declared-wins and the detection TTL are inherited unchanged), passes NO evidence (the node renders `passed-no-evidence` amber, never `done`), and no template's last node carries detect rules, so a lane can never be auto-declared `done`. `setStage` grows a `keepDetection` option — its sole caller — so the detection columns survive the advance and the card keeps showing which command moved it instead of looking like an agent's own claim. Two levers then get the evidence auto-advance cannot supply: - `codex-nudge.js` now nudges on a stage held 2+ min with no evidence, instead of on a detection sitting ahead of the declared stage — after auto-advance the old condition can never fire, since the two now match. - `ccam lanes agents install` on a codex lane writes the marker-delimited `<!-- ccam:stage:start -->` block into the lane's own AGENTS.md — the one instruction channel Codex reads — instead of copying agent templates into `.claude/agents/`, which it never loads. Appended, user prose preserved, reinstall replaces exactly that block. Verified with a real `codex exec` run: stage moved to `watching-pr` with `evidence: null` and the detection signal intact. 1364 server tests pass.
This commit is contained in:
+28
-12
@@ -1569,11 +1569,21 @@ at a live rollout file, so reading the JSONL avoids coupling to a schema the
|
||||
next Codex release renames. `rolloutFiles()` is the single function to re-point
|
||||
if that changes.
|
||||
|
||||
### Codex declared-stage reliability (`server/lib/codex-nudge.js`)
|
||||
A written detection then **also advances the lane's declared `stage`** —
|
||||
`setStage(lane.id, { stage: nodeId, keepDetection: true })`, the only place in
|
||||
CCAM where an inference moves the blue `current` ring. It is gated on
|
||||
`recordDetection` having written, so it inherits forward-only/declared-wins/TTL
|
||||
unchanged; it passes no `evidence`, so the node renders `passed-no-evidence`
|
||||
(amber) and never `done`; and `keepDetection` is the sole exception to
|
||||
`setStage`'s clearing of the detection columns, so the card still shows which
|
||||
command moved it rather than looking like an agent's own claim. No template's
|
||||
last node carries `detect` rules, so a lane can never be auto-declared `done`.
|
||||
|
||||
Detection alone only paints the amber badge; a codex lane's *declared* stage
|
||||
depends on the session calling `ccam stage` itself, so two mechanisms make
|
||||
that more likely without weakening declared-wins/inferred-never-`done`:
|
||||
### Codex evidence reliability (`server/lib/codex-nudge.js`)
|
||||
|
||||
Auto-advance moves the ring but can never supply evidence; only the session's
|
||||
own `ccam stage --evidence` call can, so three mechanisms make that more likely
|
||||
without weakening declared-wins/inferred-never-`done`:
|
||||
|
||||
- **Opening-prompt priming** (`LaneConsolePane.tsx`'s `promptPrefill`): a codex
|
||||
pane's first prompt is pre-filled with a plain-text checklist of
|
||||
@@ -1581,17 +1591,23 @@ that more likely without weakening declared-wins/inferred-never-`done`:
|
||||
— the same up-front nudge a Claude lane gets from its pre-filled
|
||||
`/ccam:ship-feature-lane` skill command, in plain text since Codex has no
|
||||
slash commands.
|
||||
- **Stale-detection nudge** (`sweepCodexNudges`, an unref'd 60 s interval from
|
||||
- **`AGENTS.md` stage block** (`server/lib/lane-agents.js`): on a `codex` lane,
|
||||
`ccam lanes agents install` writes a marker-delimited
|
||||
`<!-- ccam:stage:start -->` section into `<lane cwd>/AGENTS.md` instead of
|
||||
copying the two agent templates into `.claude/agents/`, which Codex never
|
||||
reads. Appended to an existing file (user prose preserved), and a reinstall
|
||||
replaces exactly that block.
|
||||
- **Missing-evidence nudge** (`sweepCodexNudges`, an unref'd 60 s interval from
|
||||
`server/index.js`, tunable via `DASHBOARD_CODEX_NUDGE_MS`, `0` disables):
|
||||
when a codex lane's fresh (non-stale) `detected_stage` has sat ahead of its
|
||||
declared `stage` for at least two minutes and hasn't already been nudged for
|
||||
that exact detected stage, it types a one-line `[ccam] ... run: ccam stage
|
||||
...` reminder into the lane's live tmux pane via `tmux.sendText` (skipped if
|
||||
when a codex lane's current stage has been held for at least two minutes with
|
||||
`stages[stage].evidence` still null and hasn't already been nudged for that
|
||||
exact stage, it types a one-line `[ccam] ... run: ccam stage ... --evidence
|
||||
"..."` reminder into the lane's live tmux pane via `tmux.sendText` (skipped if
|
||||
the pane isn't running an interactive program — `pty-run.js`'s
|
||||
`SHELL_COMMANDS` — since Enter there would execute the reminder as a shell
|
||||
command instead of submitting it as a chat turn). It never writes `stage` or
|
||||
`detected_stage` — any resulting declaration is still one the codex session
|
||||
made itself, through the real `ccam stage` call.
|
||||
command instead of submitting it as a chat turn). It never writes `stage`,
|
||||
`stages` or `detected_stage` — any resulting evidence is still something the
|
||||
codex session declared itself, through the real `ccam stage` call.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user