Commit Graph

12 Commits

Author SHA1 Message Date
nntrivi2001 0004cf7036 fix(lanes): removing a lane deletes its record only, never the worktree
A lane's `remove` tore down what the lane pointed at: `git worktree remove
--force`, a branch delete, and a drop of the lane's own database. One
mis-click on a dashboard row cost a whole branch's unpushed code — and the
row is the only thing AgentHub actually owns. `remove` now stops the run and
the stack, clears AgentHub's own runtime bookkeeping under
`LANES_ROOT/.state/lane<slot>/`, and deletes the row. The working tree, its
branch, git's worktree records and the database stay exactly where they are,
for the user to delete by hand when they mean to.

That also fixes #28. Removal used to route through `assertDestroyable`, whose
third check demands the cwd appear in `git worktree list`; a managed lane
whose directory git no longer listed answered `ENOTWORKTREE` ("lane is not
listed as a worktree in …") and could never be dropped from the dashboard at
all. Removal no longer asks git anything, so no preflight blocker stops it,
and the unpushed-commits force-gate now applies to `reset` alone — nothing
`remove` does can discard a commit.

The UI follows: one label for both lane kinds ("Delete lane record"), copy
that says what survives, and no Force checkbox on removal.
`removeWorktree`/`removeLaneData` remain in place as guarded primitives; no
route calls them any more.
2026-09-18 09:44:17 +07:00
nntrivi2001 876c6de6d2 fix(lanes): count every pipeline run, not one per lane
Lane Pipeline Pass Rates read `lanes.stages`, which is a snapshot of the
pass in flight: re-entering a node overwrites its record, and moving
backwards deletes every record ahead of the new stage. The report treated
one lane row as one run, so shipping ten features through a lane still
counted as one, and no node could ever exceed 1/1. History survived only
via `lane_features`, which is written when a feature is activated — a
path most users never take. The panel was not wrong, it was structurally
incapable of counting.

Add `lane_stage_events`, an append-only log written by every declaration
and every accepted detection, and aggregate from that. A run is one pass
through the template: the log starts a new one when the lane moves
backwards or leaves the template's last node. Existing snapshots are
seeded as one run each on first boot, so upgrading does not read as
"never run". Templates a lane is configured with are still listed at
zero — a pipeline going unused has to be visible as such.

Count inferred reaches too, in their own column and their own colour.
Detection stays out of the pass rate ("inference is never evidence"), but
reporting a stage the hooks demonstrably saw as "never reached" is what
made the panel look broken to anyone who works without running the
pipeline commands.

Also reject a stage naming no node of the lane's pipeline (400 EBADSTAGE,
listing the ones that exist) instead of storing it with a warning nobody
reads. A real lane had accumulated five such keys, one of them `test` on
a ship-feature lane whose CI node is `gates` — which is why that row read
never-reached while the stage had in fact been declared. Aliases still
resolve, so shorthand is unaffected.
2026-09-09 15:32:09 +07:00
nntrivi2001 17d56d0e01 refactor!: rebrand CCAM to AgentHub
Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).

Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.

Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.

Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
2026-09-07 12:09:15 +07:00
nntrivi2001 3223c8edac feat(lanes): run ONE pipeline node, from the terminal or a slash command
A pipeline was all-or-nothing: `ccam ship-full` / `ship-quick` hand the whole
thing to a skill, and a node's own `command` — added so the map would stop
hiding the driver behind its detect regexes — was display-only. Re-running just
the CI gate, or redoing the brainstorm, meant clicking copy on the node and
pasting the string into a session by hand.

`ccam lanes node [<id>] [extra…] [--print]` runs it, in the lane's own cwd. A
slash-command node spawns `claude` with it as the prompt (same shape as
`ccam ship-full`); anything else is spawned directly. No id lists every node
and its command, which is also what the two error paths print.

The node is matched by id OR alias, the same way `ccam stage` matches one:
`default` calls the step `implement` and `ship-feature` calls it `implementing`,
and one command name must not depend on which template the lane happens to be
on. That needs the template's `aliases`, which the lane payload's
`pipeline_nodes` does not carry — so the two are merged, template for the
definition, lane for the `current` marker.

Both shipped templates now fill EVERY node's `command`. The seven that were
empty are the nodes whose only work IS the declaration — a terminal `done`, a
gate another agent declares — so they get `ccam stage <id>`, which keeps all 16
(and all 8) runnable instead of leaving holes a caller has to know about.

Slash commands for the same thing, because a session should not shell out to a
nested `claude`: `/intake`, `/plan`, `/implement`, `/tests`, `/e2e`,
`/e2e-passed`, `/review`, `/qc-plan`, `/qc`, `/gate`, `/publishing`, `/pr-open`,
`/reported`, `/watching-pr`, `/merged`, `/done`, plus `/ship-full` and
`/ship-quick`. Each resolves its node through `ccam lanes node <id> --print` and
runs the result IN the current session — invoking the slash command itself when
that is what the node names, otherwise running the shell argv. Resolving at call
time rather than baking the string into the file is what stops the 18 command
files from drifting when a template changes.

Two limits are deliberate:
  - The command is split on whitespace and spawned WITHOUT a shell. A template
    is data — anyone can drop a file into DASHBOARD_PIPELINES_DIR — so its
    `command` must never be interpreted as shell. A node needing a pipe names a
    script that contains one.
  - Running a node does not declare its stage. A command executing is not proof
    the node passed, same boundary as the console never writing a lane's stage.

Verified: npm run test:server (1403, +3 covering alias matching, full command
coverage of both templates, and every command file being listed in the
manifest), npm run test:client (459), prettier and check-headers clean. Docs:
docs/LANES.md § Running ONE node, docs/PLUGINS.md commands table, README +
README.vi CLI blocks, CLAUDE.md marker.
2026-09-04 15:14:53 +07:00
nntrivi2001 fd7f57929c 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).
2026-08-27 16:38:23 +07:00
nntrivi2001 8c89e73ebd Merge remote-tracking branch 'origin/main' into feat/pipeline-driven-ship-commands
# Conflicts:
#	server/__tests__/lanes-cli.test.js
2026-08-25 11:17:33 +07:00
nntrivi2001 928512b749 test(lanes): clean up fake-gh bin tmpdirs after ci/status tests 2026-08-25 09:59:29 +07:00
nntrivi2001 a2fcd9bafc feat(cli): add ccam lanes ci status 2026-08-25 09:33:26 +07:00
nntrivi2001 2103dde889 feat(lanes): drive runs from the pipeline template's own skill
A pipeline template may now declare the slash command that drives it
(`skill`), so the choice of pipeline is the choice of workflow rather
than just a picture on the dashboard:

- `ccam ship-full "<req>"` runs the 16-node `ship-feature` template's
  skill (`/ccam:ship-feature-lane`) — through gates, QC and the senior
  gate to a published PR, watched until a human merges it.
- `ccam ship-quick "<req>"` runs the 8-node `default` template's skill
  (`/ccam:ship-feature`) — in-repo feature work that stops at the gate.

Each command puts the lane on its own template before spawning, so the
dashboard renders the right node count from the first stage instead of
after the skill self-corrects. `--print` shows the command, switches
nothing and runs nothing. The prompt reaches `claude` as one argv
element, never through a shell.

The same field feeds the Workspace run console, which pre-fills its
prompt with the lane's driving skill and never overwrites a prompt the
user has already edited.
2026-08-24 16:34:55 +07:00
nntrivi2001 dfea1a99d6 fix(tests): scrub GIT_* env vars leaking from the pre-commit hook into git-fixture tests
server/lib/update-check.js's execGit() and two test helpers
(lanes-cli.test.js, update-check.test.js) shelled out to git with an
explicit `cwd` but no `env` override. A parent git hook process (this
repo's own .husky/pre-commit, which runs `npm run test:server`) sets
GIT_DIR/GIT_INDEX_FILE in its own environment; those leak to every
child process and take precedence over `cwd` for repo discovery, so
every git command these tests ran against their throwaway tmp repos
was silently redirected at the real repo running the hook instead —
reproduced firsthand as four foreign "init"/"fixture" commits
overwriting a worktree branch mid pre-commit run. Fixes it the same
way server/lib/worktree.js already documented and did for its own git
calls: strip the GIT_* vars before exec.
2026-08-12 09:24:03 +07:00
nntrivi2001 67edda77eb feat(lanes): make the pipeline map track a skill's real progress
A lane's pipeline map only ever moved when a skill remembered to call
`ccam stage`, and the ship-feature template shipped with no detection rules
at all — so a lane driven by Superpowers skills sat at whatever stage it
last declared, and the `gates` node was never declared by anything.

Detection (`detect` rules on each node) now covers the Superpowers skill
invocations and the `ccam`/`gh` commands the ship-feature-lane skill
actually runs. It stays a safety net, not the mechanism: forward-only,
never `done`, never overriding a declaration. Two rules were deliberately
left out — `git diff` on `review` (this repo's own tests record it pinning
a lane at `review` on a real session) and anything on `merged`/`done`.

Stage vocabulary grows to 50 names over the same 16 nodes, following
Shipyard's PHASES shape: sub-states like `migration-collision`,
`e2e-scoped` and `gate-blocked` say WHY a lane sits on a node without the
map growing a node per reason. Every alias has a source — the skill
declares it, `default.json` uses it, or Shipyard's PHASES lists it.

Two silent failures fixed along the way:

- `lane.stages` is keyed by the raw declared string, so a stage declared
  under an alias lost its `--evidence` and rendered amber instead of
  green. `stageRecords` resolves each key onto its node.
- `ccam stage <typo>` stored fine and then rendered nowhere. It now warns
  on stderr while still exiting 0.

`ccam lanes pipeline` closes the gap that made all of this invisible: a
lane could only be assigned a template at creation, and no screen in the
web UI offers the choice, so every lane added from "+ Add lane" was stuck
on `default`'s 8 nodes. An unknown template id is now refused rather than
silently falling back to `default` on read.

Also merges the repo's own `ship-feature` skill into the Superpowers
workflow: it delegates planning/TDD/review/verification instead of
restating them, and declares a stage at each phase.
2026-08-07 09:34:20 +07:00
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00