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:
+11
-2
@@ -297,8 +297,15 @@ function reservedPorts(excludeLaneId = null) {
|
||||
* of where the agent now says it is has not been passed for this pass; it has
|
||||
* to be re-earned. Records under a key matching no node in the pipeline are
|
||||
* left untouched — their position is unknown, so they cannot be proven ahead.
|
||||
*
|
||||
* `keepDetection` is the ONE exception to that clearing, and exists for exactly
|
||||
* one caller: codex-ingest.js, which advances a codex lane's stage FROM its own
|
||||
* detection. There the detection is not stale evidence of something the
|
||||
* declaration superseded — it is the reason for the declaration, and the card
|
||||
* has to keep saying so, or an inferred stage becomes indistinguishable from
|
||||
* one the agent actually claimed.
|
||||
*/
|
||||
function setStage(id, { stage, status, evidence, note, result } = {}) {
|
||||
function setStage(id, { stage, status, evidence, note, result, keepDetection } = {}) {
|
||||
const lane = getLane(id);
|
||||
if (!lane) throw Object.assign(new Error(`no lane ${id}`), { code: "ENOLANE" });
|
||||
const next = stage || lane.stage;
|
||||
@@ -321,7 +328,9 @@ function setStage(id, { stage, status, evidence, note, result } = {}) {
|
||||
};
|
||||
db.prepare(
|
||||
`UPDATE lanes SET stage = ?, stage_since = ?, status = ?, stages = ?, notes = ?, updated_at = ?${
|
||||
changed ? ", detected_stage = NULL, detected_signal = NULL, detected_at = NULL" : ""
|
||||
changed && !keepDetection
|
||||
? ", detected_stage = NULL, detected_signal = NULL, detected_at = NULL"
|
||||
: ""
|
||||
}
|
||||
WHERE id = ?`
|
||||
).run(
|
||||
|
||||
Reference in New Issue
Block a user