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:
@@ -14,7 +14,11 @@
|
||||
* tool-call payload onto the same `{tool_name, tool_input}` shape a Claude hook
|
||||
* delivers, and feeds the EXISTING detect() + recordDetection() path — so a
|
||||
* codex lane paints the same amber "detected" badge under the same rules
|
||||
* (forward-only, declared-wins) and never renders `done`. Like
|
||||
* (forward-only, declared-wins) and never renders `done`. A written detection
|
||||
* then ALSO advances the lane's declared `stage` to that node, without
|
||||
* evidence: codex lanes are the one place where nothing else can move the
|
||||
* pipeline map, so the node renders `passed-no-evidence` (amber) rather than
|
||||
* `done`, and codex-nudge.js asks the session for the real evidence. Like
|
||||
* workflow-ingest.js, everything here is fail-safe: a malformed or partial
|
||||
* journal throws only locally and is skipped, and a poll pass never throws.
|
||||
*
|
||||
@@ -309,6 +313,18 @@ function ingestCodexOnce({ broadcast } = {}) {
|
||||
if (detection) {
|
||||
try {
|
||||
changed = lanesLib.recordDetection(lane.id, detection).written;
|
||||
// Auto-advance: a codex session has no hook and rarely calls `ccam
|
||||
// stage` on its own, so without this its card sits at `idle` forever
|
||||
// while the amber badge is the only thing that ever moves. Gated on
|
||||
// recordDetection having WRITTEN, so it inherits that function's
|
||||
// forward-only + declared-wins + TTL guards for free. Deliberately
|
||||
// passes NO evidence: the node renders `passed-no-evidence` (amber),
|
||||
// never `done` — detection sees a tool call, never its outcome. The
|
||||
// last node has no detect rules in any template, so this can never
|
||||
// auto-declare a lane `done`.
|
||||
if (changed) {
|
||||
lanesLib.setStage(lane.id, { stage: detection.nodeId, keepDetection: true });
|
||||
}
|
||||
} catch {
|
||||
/* lane deleted mid-pass, or sqlite busy — an inference is never worth throwing for */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user