4d9a385c5e
Client-side rendering for the per-lane runtime facts (slot, ports, database, Redis index, service liveness) added in the server-side A1/A2 work, plus the doc updates (README, CLAUDE.md, docs/API.md, client/server READMEs) describing the new profile.env keys, hook environment contract, and REST endpoints.
74 lines
8.2 KiB
Markdown
74 lines
8.2 KiB
Markdown
# Claude Code Working Guide
|
|
|
|
## Project mission
|
|
- Maintain a reliable local-first dashboard for Claude Code session monitoring.
|
|
- Preserve real-time behavior (hooks -> API -> SQLite -> WebSocket -> UI).
|
|
- Keep MCP integration production-ready for local use (`mcp/`).
|
|
|
|
## Repo map
|
|
- `server/`: Express API, hook ingestion, SQLite access, websocket broadcast (includes optional git upstream checks and `routes/updates.js`, plus `lib/workflow-ingest.js` which ingests on-disk Workflow-tool run journals — fleets that emit no hooks).
|
|
- `client/`: React + Vite UI.
|
|
- `scripts/`: hook installer/handler, import, seed, cleanup utilities. (Update detection lives server-side in `server/lib/update-check.js`; the dashboard never restarts itself — users run the printed command, surfaced in the UI and by `ccam update-check`.)
|
|
- `mcp/`: local MCP server exposing dashboard operations as tools.
|
|
|
|
## Lanes
|
|
|
|
A **lane** is a durable unit of parallel agent work — one working directory, many Claude Code sessions over time. Lanes are keyed by `cwd`, not `session_id`, so they survive session restarts and allow the dashboard to show a pipeline that persists across runs.
|
|
|
|
**Key points:**
|
|
- Sessions are bound to lanes by longest path-boundary prefix match on their `cwd` (set in hook data from `claude` CLI).
|
|
- A lane's stage is **declared** by the driving Claude session (via `ccam stage <stage> [--evidence "..."]`), not inferred by the dashboard.
|
|
- **The console never writes a lane's stage.** Declared stages come from `ccam stage` (a skill calling the CLI command), and inferred stages come from tool-event detection only. The UI's run console and the Workspace page never call `POST /api/lanes/:id/stage` — that boundary exists because the console observes tool calls but cannot see their outcomes. A lane's stage is the session's truth about progress; the console's observation of tool execution would not be proof of completion.
|
|
- **A detection expires, an evidence rule does not.** `recordDetection` skips its forward-only comparison once `detected_at` is older than `DETECTION_TTL_MS` (default 5 min), so a lane can move backwards between work sessions. That window changes only WHICH detection is current — it never relaxes declared-wins (an agent's own claim has no expiry) and never lets an inferred node render `done`.
|
|
- **Working-copy facts live at `GET /api/lanes/:id/git`, never inside `GET /api/lanes`.** That endpoint shells out to git three times; the lane list is polled and re-broadcast on every hook. A cwd that is not a readable repo returns `{available:false}` with HTTP 200 — a normal state, not a fault. Cards fetch it themselves every 30s and fail silently.
|
|
- **The Workspace console collapses with CSS, never by unmounting.** Unmounting `RunConsole` disposes the run subscription and drops a live run's rendered history.
|
|
- **CCAM does not orchestrate:** no chaining, no queue, no retry logic, no gate evaluation. The session in control makes all decisions; the dashboard records the claimed stage and shows evidence. The runtime layer (`ccam lanes up|down|hook`) does not change this — it offers *primitives* a session calls; nothing in the dashboard sequences them.
|
|
- **The runtime never writes `stage`, `status` or `notes`** — only `slot` and `ports`. `status=running` means an AGENT is working, not that a server is listening; merging the two would corrupt `classifyLiveness`. Boot failures live in `LANES_ROOT/.state/lane<slot>/last-error.json` and surface via `GET /api/lanes/:id/runtime`. Same boundary as the console-never-writes-stage rule above.
|
|
- **A lane's stack is up or down as a computed fact, never a stored one.** `runtimeFacts` re-derives it from pid files and port probes on every read. A process dies to OOM, a stray `kill`, a reboot — caching a truth CCAM does not control buys ghost state. Slot allocation is the opposite (fully controlled, must be race-free), so that one does live in the DB, under `withLaneLock` plus a partial unique index.
|
|
- **A profile's `profile.env` is parsed, never sourced.** Hooks are executed deliberately; config is only read. Sourcing arbitrary shell from a user's repository into the dashboard process would be a code-execution path. Hook names always come from the fixed allowlist in `server/lib/lane-profile.js`, never from a request.
|
|
- Pipeline templates are JSON files (`server/data/pipelines/`) with node definitions; custom templates override built-ins when `DASHBOARD_PIPELINES_DIR` is set.
|
|
- Nodes render in five states: `failed` (rejected), `current` (now), `done` (with evidence), `passed-no-evidence` (claimed or skipped, amber), `pending` (not reached).
|
|
- Liveness: a silent **watcher** (stage matching `/watch|poll/`) is dead after `LANE_DEAD_SEC` seconds (default 300); a silent **idle** lane is at rest, not dead.
|
|
- **Never `rm -rf` a lane.** Destructive lane operations (`reset`, `remove`, `purge`) go through `server/lib/worktree.js`/`server/lib/lanes.js`, never a raw filesystem delete — that's what keeps the three-check destroy guard and the DB bookkeeping in lockstep.
|
|
- **Never build a git command as a shell string.** Lane git operations use `execFile("git", [...args])` with an explicit argument array (see `server/lib/worktree.js`'s `git()` helper), never a concatenated/interpolated string passed to a shell.
|
|
- **Adopted lanes are not destroyable.** A lane with `kind === "adopted"` may never have its worktree reset or removed; "removing" one only drops the dashboard's own record of it. This is enforced in code (`assertDestroyable`'s first check) — do not add a path that bypasses it.
|
|
|
|
See `docs/LANES.md` for full guide: stage reporting, custom templates, lane actions, and the five node states.
|
|
|
|
## Non-negotiable engineering rules
|
|
- Preserve existing behavior unless explicitly asked to change it.
|
|
- Prefer minimal, reversible diffs.
|
|
- Never silently weaken safety controls around destructive actions.
|
|
- Keep docs updated when behavior, commands, file locations, or workflows change — apply the `update-project-docs` skill automatically at the end of every change-set that alters behavior, config, interfaces, events, schema, CLI commands, or features (do not wait to be asked).
|
|
- Every applicable source file you create or update (`.js/.ts/.tsx/.cjs/.mjs/.py/.sh/.css`) must start with the copyright/authorship header — file overview + the exact line `@author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>`. Formats and audit script: `.claude/skills/file-headers/` (verify with `bash .claude/skills/file-headers/scripts/check-headers.sh`). This binds every coding agent (Claude Code, Codex, or others).
|
|
|
|
## Commands you should know
|
|
- Setup: `npm run setup`
|
|
- Dev: `npm run dev`
|
|
- Prod build/start: `npm run build` then `npm start`
|
|
- Server tests: `npm run test:server`
|
|
- Client tests: `npm run test:client`
|
|
- MCP install/build/start: `npm run mcp:install`, `npm run mcp:build`, `npm run mcp:start`
|
|
- MCP typecheck: `npm run mcp:typecheck`
|
|
- CLI (after setup): `ccam <command>` — terminal access to the full dashboard surface (`bin/ccam.js`; `ccam help` lists commands)
|
|
|
|
## Testing and verification policy
|
|
- Backend changes: run `npm run test:server` before finishing.
|
|
- Frontend changes: run `npm run test:client` when relevant. This includes per-screen render snapshots (`client/src/pages/__tests__/screens.snapshot.test.tsx`). If a UI change is intentional, review the snapshot diff and regenerate baselines with `cd client && npx vitest run -u`; never blindly update snapshots to make tests pass.
|
|
- MCP changes: run `npm run mcp:typecheck` and `npm run mcp:build`.
|
|
- If you cannot run a verification step, state exactly what was not run and why.
|
|
|
|
## Change guidelines by area
|
|
- API routes: preserve response shapes unless change is requested and documented.
|
|
- Database: avoid schema changes without migration-safe logic.
|
|
- Hooks: keep fail-safe and non-blocking behavior.
|
|
- WebSocket: keep message types stable and backward-compatible.
|
|
- Documentation: include exact commands and paths; keep markdown examples runnable.
|
|
|
|
## Agent behavior
|
|
- Explore first, then implement.
|
|
- For larger tasks, propose/check a short plan before broad edits.
|
|
- Use file-specific rules in `.claude/rules/` when working in scoped areas.
|
|
- Use project skills from `.claude/skills/` for repeatable workflows.
|
|
- Use `.claude/agents/` subagents for focused review or investigation passes.
|