docs: spec and plan for codex lanes

This commit is contained in:
2026-08-27 10:35:26 +07:00
parent a75e90dea5
commit 97839b1f97
2 changed files with 995 additions and 0 deletions
@@ -0,0 +1,112 @@
# Codex lanes in the Workspace — design
Issue: [Smartgift-AI/Claude-Code-Monitor#10](https://git.smartgift.io.vn/Smartgift-AI/Claude-Code-Monitor/issues/10)
— "Workspace | Thêm Codex vào trang Workspace".
## Goal
A lane can be driven by the `codex` CLI instead of `claude`, started and watched
from the Workspace page exactly like today: same lane strip, same pipeline map,
same tmux-backed terminal console, same run history.
## Decisions (from brainstorming)
1. Scope: run Codex in the lane's tmux pane. No ingestion of `~/.codex` history.
2. The tool belongs to the **lane**, not to the run: one lane = one tool, picked
in the "Add lane" modal, not changeable afterwards. One tmux session per lane
stays `ccam-lane-<id>`.
3. Codex run controls: model, sandbox, ask-for-approval, reasoning effort.
4. Codex runs persist in `dashboard_runs` (new `tool` column) so Active runs and
Run history show them next to Claude runs.
5. The rest of the Workspace UI is unchanged.
## Data model
`server/db.js`, following the existing `ALTER TABLE lanes ADD COLUMN` pattern
(idempotent, try/catch on "duplicate column"):
- `lanes.tool TEXT NOT NULL DEFAULT 'claude'` — `'claude' | 'codex'`.
- `dashboard_runs.tool TEXT NOT NULL DEFAULT 'claude'`.
Both default to `'claude'`, so every existing row keeps today's behavior and no
response shape changes for existing consumers — `tool` is an added field.
`server/lib/lanes.js` accepts `tool` on create (`ensureLane`, worktree create),
validates it against `{claude, codex}`, and exposes it on the lane payload.
`updateLane` never writes `tool` — a lane's tool is immutable, same spirit as
"adopted lanes are not destroyable".
## Spawn
`server/lib/pty-run.js` — `buildArgv({tool, ...})` branches:
```
claude (unchanged):
claude --permission-mode <mode> --allow-dangerously-skip-permissions
[--model M] [--effort E] [--resume SID] [prompt]
codex:
codex [resume <uuid> | resume --last]
[-m MODEL] [-s SANDBOX] [-a APPROVAL]
[-c model_reasoning_effort=EFFORT] [prompt]
```
Notes:
- `resume` is a **subcommand** for Codex, so it comes first, before the flags.
`codex resume <uuid> [prompt]`; with no recorded id, `codex resume --last`.
- Allowlists (reject with `EBAD*` like today, never pass through):
- sandbox: `read-only | workspace-write | danger-full-access`
- approval: `on-request | never`
- effort: `low | medium | high | xhigh` (Codex has no `--effort` flag; it is
passed as `-c model_reasoning_effort=<value>`)
- The `--dangerously-bypass-approvals-and-sandbox` and
`--dangerously-bypass-hook-trust` flags are never emitted.
- `spawnRun` takes `tool` and records it on the run row. tmux session naming,
create-or-adopt, and the shell-prompt send path are untouched.
## API
- `POST /api/lanes/ensure`, `POST /api/lanes/worktree`: accept optional `tool`.
- `POST /api/lanes/:id/start`: accepts `sandbox` and `approval` in addition to
today's `model`/`effort`/`permissionMode`/`resume`. The **server reads the tool
from the lane row**, never from the request body, and ignores the fields that
do not belong to that tool.
- `GET /api/run/binary?tool=codex`: probes `codex` on PATH (`which`), same
`{found, path}` shape. Default (no query) stays `claude`.
## UI
- `AddLaneModal`: a second segmented toggle "Claude Code | Codex" using the
existing `Seg` component, next to the Repo/Worktree toggle.
- `LaneCard` / `LaneStripCard`: a small tool badge, shown only for `codex` lanes
so Claude lanes look exactly as before.
- `RunSetup`: fields switch on the selected lane's `tool`.
- claude: model, permission mode, effort (unchanged)
- codex: model, sandbox, approval, reasoning effort
- Codex lanes hide the Claude session picker (the sessions table holds Claude
sessions only) and offer a single "Resume last" action mapping to
`codex resume --last`.
- The Start button's disabled state uses the per-tool binary probe.
- Console, pipeline map, split view, run history: unchanged.
- All new strings go through i18n, no literals.
## Known limitations (documented, not fixed here)
Codex does not emit this dashboard's Claude Code hooks, so for a Codex lane:
- Sessions, Analytics, and cost stay empty — nothing ingests Codex usage.
- `server/lib/stage-detect.js` never paints the amber detected badge.
- Pipeline stages still work when the agent calls `ccam stage` itself (the repo
already ships `AGENTS.md` and `.codex/`).
- Lane liveness for Codex lanes comes from the tmux pane plus `dashboard_runs`.
## Testing
- `npm run test:server`: Codex argv shape (fresh, resume-by-id, resume-last,
effort mapping), allowlist rejections, `tool` persisted on lane and run,
start route ignoring foreign fields.
- `npm run test:client`: `RunSetup` field set per tool, `AddLaneModal` tool
choice reaching the API call, screen snapshots reviewed and regenerated.
- Docs to update: `docs/LANES.md` (tool per lane, Codex limitations),
`docs/API.md` (new fields), `README` lane section if it lists lane fields.