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:
2026-09-03 16:51:02 +07:00
parent 8a812a174b
commit e109d2804c
13 changed files with 318 additions and 106 deletions
+3 -1
View File
@@ -106,7 +106,9 @@ root holds all five (`qc-local`, `senior-gate-reviewer`, `backend-reviewer`,
`.claude/agents/` — the highest-priority agent source — which is what makes the
bare `subagent_type: qc-local` in the pipeline skill resolve there. One source
file, two ways to reach it; the skill names the bare form first and falls back
to the `ccam:` form.
to the `ccam:` form. On a `codex` lane the same command installs the
`<!-- ccam:stage:start -->` block in the lane's `AGENTS.md` instead — Codex
never reads `.claude/agents/`.
Because the plugin ships the hooks itself, `npm run install-hooks` is not needed
for plugin users — and must not be run alongside it. Events carry no id, so two