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.
lane-agents.test.js now drives installAgents through real lane records
(lanesLib.createLane) instead of ad-hoc git fixtures, and asserts the
agent-template source moved to agents/ (not the retired
data/agent-templates/ path). New skills-install.test.js covers the
ship-feature-lane skill copy resolving from skills/, not .claude/skills/.