fix(lanes): auto-detect and report stage for codex lanes (issue 11)

A lane on the 16-node `ship-feature` template showed an untouched pipeline
map and never declared a stage. Four independent causes, fixed at their
shared choke points:

1. Codex emits no hooks, so `touchLaneFromHook` — the only caller of
   `stage-detect.detect()`, and correctly pinned to `claude` — could never
   see a `codex` lane. New `server/lib/codex-ingest.js` tails Codex's own
   rollout journals (`$CODEX_HOME/sessions/**/rollout-*.jsonl`), maps its
   tool calls onto the hook-shaped `{tool_name, tool_input}` the matcher
   already understands, and feeds the existing detect path plus an
   idle/running status mirror under the same guard hooks use. Polled every
   `DASHBOARD_CODEX_POLL_MS` ms (default 15000, `0` disables).
2. `resolveLaneArg` matched on cwd alone, so with a claude and a codex lane
   at one directory (the composite `idx_lanes_cwd_tool`) a `ccam stage`
   call could drive the sibling lane's card. It now resolves by
   `--lane` → `CCAM_LANE_ID` → `--tool`/harness marker → owning lane, and
   errors naming both candidates rather than picking one. Every pane the
   dashboard starts carries `CCAM_LANE_ID` (`tmux new-session -e`, or an
   assignment prefix when typing into a live shell pane).
3. `recordDetection`'s declared-wins veto had no exit: a lane left at the
   pipeline's last node rejected every detection for the rest of its life.
   A declaration on the LAST node no longer vetoes — the pipeline is over
   there, so a later detection is new work. Nothing else is relaxed, and
   detection still never writes `stage` nor renders `done`.
4. The run console pre-filled the pipeline's Claude slash command into
   codex panes, which Codex reads as prose — so the skill that would have
   called `ccam stage` never ran. Claude lanes only now.

A codex lane's *declared* stage still depends on the session calling
`ccam stage`; `AGENTS.md` is the only lever CCAM has, so it now says so.

Verified: server 1345/1345, client 441/441, and end-to-end against a real
codex lane row (detected_stage/detected_signal set, declared stage
untouched, no node rendered done).
This commit is contained in:
2026-08-27 16:38:23 +07:00
parent b1734a492f
commit fd7f57929c
22 changed files with 2343 additions and 35 deletions
+5 -2
View File
@@ -106,8 +106,11 @@ Mỗi lane còn mang theo CLI điều khiển nó — `claude` (mặc định) h
chọn trong modal Add lane và cố định suốt vòng đời của lane. Lane Codex có panel
chạy với các lựa chọn riêng của Codex (sandbox, approval, reasoning effort) và
resume bằng `codex resume --last`. Codex không bắn hook của dashboard này, nên
lane Codex vẫn ghi lịch sử chạy nhưng không xuất hiện ở Sessions, Analytics và
stage detection.
lane Codex vẫn ghi lịch sử chạy nhưng không xuất hiện ở Sessions và Analytics.
**Stage detection thì vẫn chạy**, đọc từ rollout journal của chính Codex trên đĩa
(`$CODEX_HOME/sessions/**/rollout-*.jsonl`, poll mỗi `DASHBOARD_CODEX_POLL_MS` ms,
mặc định 15000) — thứ lane Codex không tự có là stage **khai báo**, vì chỉ chính
session đó mới gọi được `ccam stage`.
Stage khai báo (declared) đến từ `ccam stage`. Stage suy luận (inferred) đến
từ tool event và hết hạn sau `DETECTION_TTL_MS` (mặc định 5 phút), nên lane có