refactor(lanes): drop the workflow skills and Superpowers link; pipelines go dormant
The plugin stops shipping its workflow layer: - skills/ship-feature and skills/ship-feature-lane, the 18 stage/ship slash commands (plugin.json keeps agenthub-doctor/update/open), and all four agents/ (qc-local, senior-gate-reviewer, backend-reviewer, frontend-reviewer). - CLI `agenthub skills install`, `agenthub lanes agents install`, `agenthub ship-full|ship-quick`; routes POST /api/skills/install and POST /api/lanes/:id/agents/install; the Settings button, the lane Tools menu item and the Add-lane setup step that called them. - The Superpowers auto-install: server/lib/skill-plugins.js, the bootstrap's autoInstallSkillPlugins / skillPluginsSettled gate and the doctor row. Pipelines are kept but dormant: server/data/pipelines/ is deleted, so no template ships. `default` resolves to EMPTY_PIPELINE (no nodes, progress 0), createLane still accepts it, POST /api/lanes/:id/stage answers 400 ENOPIPELINE, and the Workspace hides the stage timer, pickers, PipelineMap and strip stage chip for a lane with no nodes. A template dropped into DASHBOARD_PIPELINES_DIR turns it all back on. The server suite loads its templates from server/__tests__/fixtures/pipelines/. Docs: CLAUDE.md (marker, retired rules), README(.vi), INSTALL, ARCHITECTURE, docs/LANES|PLUGINS|CLI|API, agenthub-introduction.html.
This commit is contained in:
+68
-321
@@ -32,9 +32,9 @@ agenthub lanes add --repo /path/to/repo --title "My Feature" --base main --slug
|
||||
|
||||
`--title`, `--base`, and `--slug` are optional. The CLI waits for background provisioning to finish and reports either the ready lane or its failure notes.
|
||||
|
||||
Adopting your main repo (`agenthub lanes add --cwd $(pwd)`) is also how you get stage detection working for sessions that work directly in it rather than in a worktree — see "Superpowers skill invocations" below. Once adopted, that lane's own card lists every managed-worktree lane whose `source_repo` matches its `cwd`, each a clickable link to jump to that lane.
|
||||
Adopting your main repo (`agenthub lanes add --cwd $(pwd)`) is also how you get stage detection working for sessions that work directly in it rather than in a worktree (once a pipeline template is installed) — see "Stage detection" below. Once adopted, that lane's own card lists every managed-worktree lane whose `source_repo` matches its `cwd`, each a clickable link to jump to that lane.
|
||||
|
||||
Adding a lane through the dashboard's "+ Add lane" flow also auto-runs, best-effort, in parallel: `agenthub lanes profile init` (only if a Node.js project is detected — most repos won't be, and that's a normal outcome, not a failure), `agenthub lanes agents install`, and `agenthub lanes mcp sync`. None of the three blocks the lane from being created or from each other — a lane whose repo has no MCP servers configured, for instance, still gets created and is still usable, just without a synced `.mcp.json`. The modal shows a ✓/✗ summary of the three results and stays open until dismissed (Cancel/X) — it does not auto-close. Run any of the three manually later (from the lane's own card, or the CLI) if the automatic attempt didn't apply.
|
||||
Adding a lane through the dashboard's "+ Add lane" flow also auto-runs, best-effort, in parallel: `agenthub lanes profile init` (only if a Node.js project is detected — most repos won't be, and that's a normal outcome, not a failure), `agenthub lanes mcp sync`, and the AI Router pick. None of the three blocks the lane from being created or from each other — a lane whose repo has no MCP servers configured, for instance, still gets created and is still usable, just without a synced `.mcp.json`. The modal shows a ✓/✗ summary of the three results and stays open until dismissed (Cancel/X) — it does not auto-close. Run any of the three manually later (from the lane's own card, or the CLI) if the automatic attempt didn't apply.
|
||||
|
||||
### The "+ Add lane" modal
|
||||
|
||||
@@ -118,9 +118,8 @@ Codex does not emit Claude Code hooks (sessions, agent events, tool-execution tr
|
||||
|
||||
**These are not bugs; they reflect Codex's architecture.** Liveness is tracked by the lane's tmux pane (if the stack is up) and `dashboard_runs` table (recording every invocation). Cost tracking for Codex runs would require parsing Codex's own logs, a separate design not yet implemented.
|
||||
|
||||
**Codex lanes carry no pipeline.** Nothing infers their stage, nothing types
|
||||
a stage reminder into their pane, and `agenthub lanes agents install` installs
|
||||
nothing on them (`installed: []`). The one thing AgentHub reads from Codex is
|
||||
**Codex lanes carry no pipeline.** Nothing infers their stage and nothing types
|
||||
a stage reminder into their pane. The one thing AgentHub reads from Codex is
|
||||
its running/idle status — see "Stage detection" for the journal poll that
|
||||
mirrors it. Every server start also wipes what older builds left behind on a
|
||||
codex lane — its stage columns, stage log (`lane_stage_events`) and feature
|
||||
@@ -395,8 +394,8 @@ The dashboard web UI merges lanes and runs into a single **Workspace** page acce
|
||||
- **No lanes yet** — the strip is replaced by a single banner (`lane-empty`): icon, one line of what a lane is, the CLI equivalent (`agenthub lanes add --cwd $(pwd)`) and an **Add lane** affordance. The whole banner IS the button — clicking anywhere on it opens the Add-lane modal, so there is no separate button to hit. The strip itself — the dashed add tile plus the lane tiles — only renders once at least one lane exists, because an add tile beside a stray sentence read as a half-drawn row. The banner stays compact on purpose: the run setup form below it is what needs the height.
|
||||
- **Reordering the strip** — drag a tile onto another to move it there, or press **Alt+←/→** with a tile focused (the keyboard path; HTML5 drag-and-drop has no touch support and does not auto-scroll an overflowing strip). The order is a **per-viewer preference**, not a lane fact: it persists to `localStorage` (`agenthub.workspace.laneOrder`) exactly like the split-view layout beside it, and writes nothing server-side — `GET /api/lanes` and `agenthub lanes` keep answering in id order, and the order does not follow you to another browser. A lane the stored order has never seen (created since, or created in another tab) lands at the END rather than wherever a stale index would put it; a stored id whose lane is gone is skipped.
|
||||
- **Renaming a lane** — the detail header's name is a button: click it (a pencil appears on hover) to edit in place. **Enter** saves via `PATCH /api/lanes/:id` with `{title}`, **Escape** and clicking away both cancel, and saving an empty name clears the title, which puts the lane's `cwd` back in its place. Renaming is display-only — a managed lane's branch name was slugified from its title once, at creation.
|
||||
- **Lane-detail header row** — one row, and every per-lane control lives on it: liveness + status, the lane's name (no "Lane 23 ·" prefix — the id is on the strip tile), time-on-stage, the pipeline picker, the Model (AI Router) select, the feature picker (there are no stage chips here — the declared stage and the `auto: <stage>` detection both render in the PipelineMap directly below, so the chips were a second, smaller copy of the same two facts), and on the right the **Tools** menu (agents install / mcp sync / sync-base check / create-update readme, hidden when the lane declares no runtime profile), the info toggle, the split-view toggle and Remove. Tools sits here rather than in Settings because every one of its actions runs against one lane's `cwd` — in Settings it would need a lane picker just to say which. **Create/update readme file** (Claude lanes only — Codex has no slash commands) starts the lane's terminal run with `/sg-all:sg-md-writing` as its first message, so the lane opens already writing/refreshing its `CLAUDE.md`/`AGENTS.md` set; it needs the `sg-all` plugin installed in Claude Code, and without it the session just reports an unknown command. It used to be a checkbox in the Add-lane modal: creating a lane and writing its docs are separate decisions, and the second is worth re-running long after the first.
|
||||
- **Detail panel** — the selected lane's declared stage, its inferred stage when detection leads, a full-width pipeline map, and a legend naming all five node states plus the dashed-amber inferred treatment.
|
||||
- **Lane-detail header row** — one row, and every per-lane control lives on it: liveness + status, the lane's name (no "Lane 23 ·" prefix — the id is on the strip tile), time-on-stage, the pipeline picker, the Model (AI Router) select, the feature picker (time-on-stage and both pickers only while a pipeline template is installed; there are no stage chips here — the declared stage and the `auto: <stage>` detection both render in the PipelineMap directly below, so the chips were a second, smaller copy of the same two facts), and on the right the **Tools** menu (mcp sync / sync-base check / create-update readme, hidden when the lane declares no runtime profile), the info toggle, the split-view toggle and Remove. Tools sits here rather than in Settings because every one of its actions runs against one lane's `cwd` — in Settings it would need a lane picker just to say which. **Create/update readme file** (Claude lanes only — Codex has no slash commands) starts the lane's terminal run with `/sg-all:sg-md-writing` as its first message, so the lane opens already writing/refreshing its `CLAUDE.md`/`AGENTS.md` set; it needs the `sg-all` plugin installed in Claude Code, and without it the session just reports an unknown command. It used to be a checkbox in the Add-lane modal: creating a lane and writing its docs are separate decisions, and the second is worth re-running long after the first.
|
||||
- **Detail panel** — the selected lane's declared stage, its inferred stage when detection leads, a full-width pipeline map, and a legend naming all five node states plus the dashed-amber inferred treatment Hidden while no pipeline template is installed (see "Pipeline stages and the five node states").
|
||||
- **Terminal** — a real interactive terminal (xterm.js) displaying the tmux session's PTY output, with full support for interactive commands, editors, and pagers. A live run keeps its rendered history and scroll position when scrolling. It grabs the keyboard the moment it appears, so typing goes into the pane without clicking it first, and when the pane's process ends — a double Ctrl+C out of `claude`, `exit`, a crash — the console returns to the run setup form on its own, with no "New run" click.
|
||||
- **Clipboard in a pane** — the lane's tmux session is started with the graphical session's own `DISPLAY`/`WAYLAND_DISPLAY`/`XAUTHORITY`/`XDG_RUNTIME_DIR` (read from `systemctl --user show-environment`, this process's own values winning; `server/lib/desktop-env.js`). The dashboard normally runs as a systemd *user* service, which has none of them, so a pane it started had no clipboard at all and `claude` answered "no image found in clipboard" on every image paste while the same CLI pasted fine in a terminal. Only those four names are forwarded, and only into panes this dashboard spawns — an already-running pane keeps the environment it was started with, so it needs a restart to gain one.
|
||||
- **Split view** — a layout toggle (one pane / two side by side / a 2×2 grid, drawn as icons rather than the words, with the count kept as each button's `aria-label` and tooltip) renders that many terminal panes side by side (`grid-cols-2` for 2, a 2×2 grid for 4). EVERY pane is bound to the lane strip's selection — there is no per-pane lane picker — and each pane owns its own tmux session on that lane: pane 0 is the lane's `agenthub-lane-<id>` (its `run_id`), panes 1–3 are `agenthub-lane-<id>-<slot>` (`POST /api/lanes/:id/start` with `slot`). A pane's **Active runs** leaves out the runs the other panes are showing, so one terminal is never attached twice side by side. Only the layout persists to `localStorage` (`agenthub.workspace.splitView`); a stored per-pane lane id from an older build is ignored.
|
||||
@@ -746,7 +745,9 @@ The dashboard web UI shows each lane as a card in a grid, with the selected lane
|
||||
|
||||
## Pipeline stages and the five node states
|
||||
|
||||
A lane moves through stages defined in a **pipeline template** (see "Custom pipeline templates" below). The default pipeline has eight stages: `intake`, `plan`, `implement`, `tests`, `review`, `gate`, `ship`, and `done`.
|
||||
> **Pipelines are dormant.** AgentHub ships no pipeline template. Until you install one (drop a template JSON into `DASHBOARD_PIPELINES_DIR` — see "Custom pipeline templates" below), every lane sits on an empty `default` pipeline: no nodes, progress `0`. `GET /api/lanes/pipelines` returns `{"pipelines": []}`, `agenthub stage <x>` (and `POST /api/lanes/:id/stage`) answers `400 ENOPIPELINE`, and the Workspace hides the time-on-stage timer, the pipeline and feature pickers and the PipelineMap — the lane tile shows its tool tag instead of a stage chip. Install a template and all of it comes back; a template with id `default` becomes every lane's default. Further development of pipelines is planned. The rest of this section, and the stage sections after it, describe a lane on an installed template.
|
||||
|
||||
A lane moves through stages defined in a **pipeline template** (see "Custom pipeline templates" below).
|
||||
|
||||
**A lane is created on `default` unless told otherwise.** The "+ Add lane" modal does not ask — it always creates on `default`. Pick another with `agenthub lanes add --pipeline <id>` at creation, or change it afterwards from the *Pipeline template* select in the lane's detail header in Workspace.
|
||||
|
||||
@@ -754,63 +755,31 @@ For a lane that already exists, switch it from the terminal:
|
||||
|
||||
```bash
|
||||
agenthub lanes pipeline # which template this lane uses, and what else exists
|
||||
agenthub lanes pipeline ship-feature # switch it
|
||||
agenthub lanes pipeline ml-training # switch it (any installed template id)
|
||||
```
|
||||
|
||||
or from the dashboard: the pipeline-template select next to the lane's title in the Workspace detail panel does the same `PATCH /api/lanes/:id` the CLI uses, live (disabled while viewing an archived feature snapshot).
|
||||
|
||||
**The template names the skill that drives it.** `"skill": "/agenthub:ship-feature-lane"` on the 16-node `ship-feature` template, `"skill": "/agenthub:ship-feature"` on the 8-node `default` one. It rides along on every lane payload as `pipeline_skill`, and from a terminal there is one command per depth of work — the command you type picks the pipeline, so no `agenthub lanes pipeline` step first:
|
||||
|
||||
```bash
|
||||
cd ~/lanes/myrepo__feat-export
|
||||
|
||||
# 16 stages: ends in a published PR, watched until a human merges it
|
||||
agenthub ship-full "add an export button"
|
||||
# → lane switched to `ship-feature`, then: claude "/agenthub:ship-feature-lane add an export button"
|
||||
|
||||
# 8 stages: in-repo feature work, stops at review/gate — no PR, no browser QC
|
||||
agenthub ship-quick "fix the CSV separator"
|
||||
# → lane switched to `default`, then: claude "/agenthub:ship-feature fix the CSV separator"
|
||||
|
||||
agenthub ship-full "add an export button" --print # show the command, switch nothing, run nothing
|
||||
```
|
||||
|
||||
| | `agenthub ship-full` | `agenthub ship-quick` |
|
||||
|---|---|---|
|
||||
| Template | `ship-feature` (16 nodes) | `default` (8 nodes) |
|
||||
| Skill | `/agenthub:ship-feature-lane` | `/agenthub:ship-feature` |
|
||||
| Ends at | PR open on `development`, watched until merged | `ship` in the working copy |
|
||||
| Includes | unit-test agent, CI gates, e2e, code review, local browser QC, senior GO/NO-GO | plan, implement, tests, review, gate |
|
||||
| Use when | the work should reach reviewers | the work stays local |
|
||||
|
||||
The lane is resolved from the working directory, so no id is needed (pass one to target another lane). Each command puts the lane on its own template *before* spawning, so the dashboard renders the right node count from the first stage rather than after the skill self-corrects; `--print` switches nothing. The requirement is handed to `claude` as ONE argv element — never through a shell — so quotes, `$` and `;` in it are text. A template with no `skill` field makes the command refuse rather than guess.
|
||||
A template may name the slash command that drives it (its optional `skill` field). It rides along on every lane payload as `pipeline_skill`; the dashboard never executes it.
|
||||
|
||||
### Running ONE node
|
||||
|
||||
`agenthub ship-full` / `agenthub ship-quick` hand the whole pipeline to a skill. When you only want one step — re-run the CI gate, redo the brainstorm, open the PR by hand — run that node's own `command`:
|
||||
When you only want one step — re-run a CI gate, redo a brainstorm — run that node's own `command`:
|
||||
|
||||
```bash
|
||||
agenthub lanes node # list every node of this lane's pipeline and its command
|
||||
agenthub lanes node intake # → claude "/superpowers:brainstorming"
|
||||
agenthub lanes node intake the export button # extra words are appended to the prompt
|
||||
agenthub lanes node review # → claude "/code-review"
|
||||
agenthub lanes node gates # → agenthub lanes hook ci-gate
|
||||
agenthub lanes node pr-open --print # show the argv, run nothing
|
||||
agenthub lanes node # list every node of this lane's pipeline and its command
|
||||
agenthub lanes node <node-id> # run that node's command in the lane's cwd
|
||||
agenthub lanes node <node-id> extra words # extra words are appended to the prompt
|
||||
agenthub lanes node <node-id> --print # show the argv, run nothing
|
||||
```
|
||||
|
||||
Both shipped templates give **every** node a command, so any of the 16 (or 8) is runnable. A node starting with `/` is spawned as `claude "<command> <extra args>"`; anything else is spawned directly. The lane comes from the working directory as usual, and the command always runs in the lane's own `cwd`.
|
||||
|
||||
The node is matched by **id or alias**, so one name works on either template: `default` calls the step `implement` and `ship-feature` calls it `implementing`, and `agenthub lanes node implement` finds it on both.
|
||||
|
||||
**From inside a Claude session, use the slash commands instead.** The plugin ships one per node — `/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` for the whole pipeline. Each resolves the node's command through `agenthub lanes node <id> --print` and runs it *in the current session*: a slash-command node is invoked directly (never as a nested `claude` process), anything else runs as a shell argv in the lane's cwd. Extra words are appended to the prompt: `/intake the export button`.
|
||||
A node is runnable when its template gives it a `command`. A command starting with `/` is spawned as `claude "<command> <extra args>"`; anything else is spawned directly. The lane comes from the working directory as usual, and the command always runs in the lane's own `cwd`. The node is matched by **id or alias**.
|
||||
|
||||
Two deliberate limits:
|
||||
|
||||
- **The command is split on whitespace and spawned without a shell.** A template is data — files anyone can drop into `DASHBOARD_PIPELINES_DIR` — so its `command` is never interpreted as shell. A node needing a pipe or a redirect names a script that contains them.
|
||||
- **Running a node does not declare its stage.** A command executing is not proof the node passed — the same reason the run console never writes a lane's stage. Declare with `agenthub stage <id>` when the work is actually done. (The exception is a node whose command *is* `agenthub stage <id>`: there the declaration is the whole step.)
|
||||
|
||||
**The template also decides what the Workspace run console pre-fills.** A template may name the slash command that drives it — `"skill": "/agenthub:ship-feature-lane"` on the 16-node `ship-feature` template, `"skill": "/agenthub:ship-feature"` on the 8-node `default` one. That string rides along on every lane payload as `pipeline_skill`, and the run console starts its prompt box with it (plus a trailing space) so you only type the requirement: pick the 16-node template and Run starts the 16-step skill, pick the 8-node one and it starts the 8-step skill. The dashboard never executes it — it is text in a prompt box, exactly as if you had typed it. A prompt you have already edited is never overwritten by a later template switch, and a template with no `skill` field pre-fills nothing.
|
||||
|
||||
Switching re-resolves the lane's existing declared `stage` against the new node list. A stage the old template knew may resolve to nothing in the new one; the switch warns when that happens (both CLI and UI), and the next `agenthub stage` fixes it.
|
||||
|
||||
### Aggregate pass rates across lanes
|
||||
@@ -859,8 +828,6 @@ offering every other lane even while the counts show only one.
|
||||
|
||||
`agenthub lanes shell` attaches a real terminal to the exact tmux session the dashboard's Start/Resume buttons use for this lane (`agenthub-lane-<id>`), creating it if it doesn't exist yet. Type `claude` inside it like any normal terminal session — the dashboard's Workspace terminal view is just another client attached to the same tmux session, so both stay in sync live.
|
||||
|
||||
**In practice you never have to pick correctly at creation.** The template only has to match whichever skill is actually driving the lane, and the skill enforces that itself: `ship-feature-lane` runs `agenthub lanes pipeline ship-feature` and `ship-feature` runs `agenthub lanes pipeline default` before their first `agenthub stage` call — a no-op if the lane is already on that template, a self-correction if it isn't. A human chatting with the lane never needs to open the picker; whichever skill gets invoked decides the template.
|
||||
|
||||
The dashboard renders every node in the pipeline in one of five **states**:
|
||||
|
||||
| State | Color | Meaning |
|
||||
@@ -876,13 +843,13 @@ The dashboard renders every node in the pipeline in one of five **states**:
|
||||
A stage in this state was **claimed but not proven**. Two scenarios:
|
||||
|
||||
1. **Declared but no artifact left.** A skill called `agenthub stage review` without `--evidence`, claiming the stage happened but leaving no proof that work occurred.
|
||||
2. **Implicitly skipped.** The lane jumped from `plan` to `implement` (to stage index 2), leaving `intake` (index 0) in `passed-no-evidence` state.
|
||||
2. **Implicitly skipped.** On a template whose nodes start `intake`, `plan`, `implement`, the lane jumped from `plan` to `implement` (to stage index 2), leaving `intake` (index 0) in `passed-no-evidence` state.
|
||||
|
||||
In both cases, the stage is not **done** — it's incomplete or untested. Amber signals that the agent claimed to have done this work but the dashboard has no evidence to show it was actually completed. This matters in a pipeline: if your gate checks `done` not `passed-no-evidence`, a lane stuck in amber will block until evidence is provided.
|
||||
|
||||
## Reporting a stage
|
||||
|
||||
`agenthub stage` is not skill-only. Any Claude session working inside an adopted lane's cwd — including a plain chat session where no skill was ever invoked — should call it on real stage transitions. Tool-event detection (see "Inferred vs. declared" below) can only paint the amber "detected" badge; it never moves the blue `current` ring. A lane worked entirely through plain chat that never calls `agenthub stage` will keep showing whatever stage it was last declared at (often `intake`), regardless of how much real work happens.
|
||||
`agenthub stage` is not skill-only. Any Claude session working inside an adopted lane's cwd — including a plain chat session where no skill was ever invoked — should call it on real stage transitions. Tool-event detection (see "Inferred vs. declared" below) can only paint the amber "detected" badge; it never moves the blue `current` ring. A lane worked entirely through plain chat that never calls `agenthub stage` will keep showing whatever stage it was last declared at, regardless of how much real work happens. With no pipeline template installed there is nothing to declare: the command answers `400 ENOPIPELINE`.
|
||||
|
||||
When a skill — or a plain chat session — reaches a significant milestone, it reports the current stage to the dashboard using `agenthub stage`:
|
||||
|
||||
@@ -904,18 +871,9 @@ agenthub stage <stage> [--lane <id>] [--cwd <path>] [--tool claude|codex] [--sta
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `<stage>` (required): the pipeline stage name or alias. The default pipeline aliases are:
|
||||
- `intake` / `assigned` / `claimed` / `start`
|
||||
- `plan` / `planning` / `brainstorm` / `design`
|
||||
- `implement` / `implementing` / `coding` / `build`
|
||||
- `tests` / `testing` / `unit` / `gates` / `pre-push-gate`
|
||||
- `review` / `reviewing` / `code-review` / `self-review`
|
||||
- `gate` / `verify` / `verification` / `sr-gate` / `gate-blocked`
|
||||
- `ship` / `pr` / `pr-open` / `publishing` / `commit` / `push`
|
||||
- `done` / `complete` / `completed` / `merged`
|
||||
|
||||
Aliases are **per template** — the `ship-feature` pipeline has its own set
|
||||
(see "Pipeline template: ship-feature" below). A name matching no node and no
|
||||
- `<stage>` (required): a node id or one of its aliases, as the lane's
|
||||
pipeline template defines them (see "Custom template format"). Aliases are
|
||||
**per template**. A name matching no node and no
|
||||
alias is still recorded verbatim, but `phaseIdx` then resolves it to nothing:
|
||||
no node renders `current` and progress reads `0`. The CLI prints a warning to
|
||||
stderr and still exits `0` in that case — a typo must not break a
|
||||
@@ -972,8 +930,7 @@ Such keys only exist on lanes that predate validation: `POST
|
||||
/api/lanes/:id/stage` now rejects a stage naming no node of the lane's pipeline
|
||||
with **400 `EBADSTAGE`** and lists the ones that exist. Aliases still resolve,
|
||||
so this catches typos and names borrowed from another template — `agenthub
|
||||
stage test` on a `ship-feature` lane, whose CI node is `gates` — which used to
|
||||
be stored, warned about on stderr, and then counted nowhere.
|
||||
stage test` on a template whose CI node is `gates` — which used to be stored, warned about on stderr, and then counted nowhere.
|
||||
|
||||
Every declaration is also appended to `lane_stage_events`, the append-only log
|
||||
the pass-rate report reads. `lanes.stages` describes the pass in flight and is
|
||||
@@ -1038,14 +995,11 @@ agenthub stage tests --evidence "Pre-push gate: all linters passing" --result pa
|
||||
|
||||
## Custom pipeline templates
|
||||
|
||||
The default pipeline is suitable for feature work: `intake` → `plan` → `implement` → `tests` → `review` → `gate` → `ship` → `done`. For different workflows (data pipelines, research phases, deployment stages), you can provide custom templates.
|
||||
AgentHub ships no template, so a custom template is the only way to turn the pipeline map, stage reporting and stage detection on. A template models whatever workflow you run — feature work, data pipelines, research phases, deployment stages.
|
||||
|
||||
### Where templates come from
|
||||
|
||||
1. **Built-in:** `server/data/pipelines/default.json` (always available)
|
||||
2. **User override:** directory specified by the `DASHBOARD_PIPELINES_DIR` environment variable
|
||||
|
||||
If both exist, **files in the user directory override built-ins** with the same `id`.
|
||||
Every `*.json` file in the directory named by the `DASHBOARD_PIPELINES_DIR` environment variable is loaded once at server start; a malformed file is skipped, never fatal. A template whose `id` is `default` becomes the pipeline of every lane that names no other.
|
||||
|
||||
### Custom template format
|
||||
|
||||
@@ -1070,14 +1024,15 @@ Create a JSON file in your custom pipeline directory (e.g., `~/.agenthub/pipelin
|
||||
|
||||
- **id** (string, required): unique identifier for this template
|
||||
- **name** (string, optional): display name shown in the UI
|
||||
- **skill** (string, optional): the slash command that drives this pipeline (e.g. `"/agenthub:ship-feature-lane"`). The Workspace run console pre-fills its prompt with it for lanes on this template; omit it and the prompt starts empty. Never executed by the dashboard.
|
||||
- **skill** (string, optional): the slash command that drives this pipeline (e.g. `"/my-training-skill"`). Carried on every lane payload as `pipeline_skill`. Never executed by the dashboard.
|
||||
- **nodes** (array, required): list of pipeline stages, in order
|
||||
- **id** (string): stage identifier (used in `agenthub stage`)
|
||||
- **label** (string, optional): display name in the UI; defaults to `id` if omitted
|
||||
- **icon** (string, optional): unicode emoji or single character shown before the stage name
|
||||
- **gate** (boolean, optional): `true` if this stage blocks unless explicitly passed; defaults to `false`
|
||||
- **command** (string, optional): the one command that drives THIS stage — a slash command (`"/superpowers:writing-plans"`) or a shell one (`"agenthub lanes hook ci-gate"`). Where `skill` above names the driver for the whole pipeline, this narrows it to the step, so a reader of the pipeline map sees what moves each node instead of inferring it from the node's `detect` regexes. Omit it (or leave it empty) for a node with no single driver; both shipped templates fill every node, using `"agenthub stage <id>"` for the nodes whose only "work" is the declaration itself (a terminal `done`, a gate another agent declares). Surfaced in the node's tooltip on a lane card and as copyable text per node on the Workflows page, and run by `agenthub lanes node <id>` (below); **never executed by the dashboard**, same contract as `skill`.
|
||||
- **aliases** (array, optional): alternative names for the stage (e.g., `["training", "fit"]` lets you call `agenthub stage training`)
|
||||
- **command** (string, optional): the one command that drives THIS stage — a slash command (`"/my-training-skill"`) or a shell one (`"agenthub lanes hook ci-gate"`). Where `skill` above names the driver for the whole pipeline, this narrows it to the step, so a reader of the pipeline map sees what moves each node instead of inferring it from the node's `detect` regexes. Omit it (or leave it empty) for a node with no single driver; use `"agenthub stage <id>"` for a node whose only "work" is the declaration itself (a terminal `done`, a gate another agent declares). Surfaced in the node's tooltip on a lane card and as copyable text per node on the Workflows page, and run by `agenthub lanes node <id>` (below); **never executed by the dashboard**, same contract as `skill`.
|
||||
- **aliases** (array, optional): alternative names for the stage (e.g., `["training", "fit"]` lets you call `agenthub stage training`). An alias may also name a **sub-state** — *why* the lane sits on a node — without adding a node to the map. Two constraints: no alias may collide with another node's id or alias (`phaseIdx` takes the FIRST match, so a duplicate silently resolves a declaration onto the wrong node), and all aliases of one node share one record slot (`stageRecords` resolves each declared key onto its node, and the canonical id's record wins), so a sub-state that needs its own `--evidence` has to be a real node.
|
||||
- **detect** (array, optional): stage-detection rules for this node — see "Stage detection → Where the rules live".
|
||||
|
||||
Then set the environment variable:
|
||||
|
||||
@@ -1091,7 +1046,7 @@ When you create a new lane, you can optionally specify which pipeline to use:
|
||||
agenthub lanes add --cwd /path/to/ml-repo --title "Training run #1" --pipeline ml-training
|
||||
```
|
||||
|
||||
(The pipeline is selected at creation time. Lanes created without `--pipeline` default to the `default` pipeline.)
|
||||
(The pipeline is selected at creation time. Lanes created without `--pipeline` default to the `default` pipeline — yours if you installed one with that id, otherwise the empty one.)
|
||||
|
||||
|
||||
## Per-feature state and archive
|
||||
@@ -1196,9 +1151,8 @@ leads the declared one (see "Viewing lanes" below), and `LaneStripCard.tsx`'s
|
||||
and the browser never disagree about which one is the headline. Both locate the
|
||||
declaration by the node the server already resolved it onto (the one whose
|
||||
`state` is `current`), never by looking `lane.stage` up as a node id: most of
|
||||
what the ship-feature skill declares is an ALIAS (`tests`, `booting`, `ship`,
|
||||
`gate-blocked`), which is not an id, and an id lookup scores it -1 — every
|
||||
detection, however far behind, would then read as leading.
|
||||
what a skill declares may be an ALIAS, which is not an id, and an id lookup
|
||||
scores it -1 — every detection, however far behind, would then read as leading.
|
||||
|
||||
### Which lane a signal is credited to
|
||||
|
||||
@@ -1241,7 +1195,7 @@ contain whole code blocks:
|
||||
Each rule is tested against these fields **one at a time**, never against a join
|
||||
of them. An `Agent` event really carries `description` + `prompt` +
|
||||
`subagent_type` together, so a rule anchored to the agent's identity —
|
||||
`{"tool": "Agent", "match": "^Explore$"}` in `ship-feature.json` — would never
|
||||
`{"tool": "Agent", "match": "^Explore$"}` — would never
|
||||
fire against a concatenation of all three, and a rule could otherwise match
|
||||
across the seam between two unrelated fields. Write anchored rules against the
|
||||
one field that identifies the tool call.
|
||||
@@ -1257,18 +1211,6 @@ to the flattened input. Either way the result is capped at 120 characters
|
||||
lane's tooltip sometimes shows a file path or a skill name rather than a shell
|
||||
command: it's whichever of the fields above the matching rule's `tool` carried.
|
||||
|
||||
### Superpowers skill invocations
|
||||
|
||||
The built-in `default` pipeline's `intake`, `plan`, `implement`, `review`, and
|
||||
`ship` nodes each carry a `{"tool": "Skill", "match": "..."}` rule matching the
|
||||
workflow skill names they expect (`brainstorming`, `writing-plans`,
|
||||
`executing-plans`/`subagent-driven-development`, Claude Code's own
|
||||
`code-review` plus `requesting-code-review`/`receiving-code-review`,
|
||||
`finishing-a-development-branch`). Invoking one of
|
||||
these skills is a much stronger signal than a matched Bash command, but it is
|
||||
still detection, not declaration — it renders dashed amber and never `done`,
|
||||
same as every other detected stage.
|
||||
|
||||
Detection only ever attributes to a lane whose `cwd` matches the hook's
|
||||
session `cwd` (see "Which lane a signal is credited to" above). A session
|
||||
working directly in a source repo that was never itself adopted as a lane —
|
||||
@@ -1308,90 +1250,36 @@ read per call, so no restart is needed for a test harness that sets it.
|
||||
### Where the rules live
|
||||
|
||||
Detection rules are not code — they live in the pipeline template itself, as
|
||||
a `detect` array on each node. The built-in `default.json`
|
||||
(`server/data/pipelines/default.json`) ships these rules today, node by node:
|
||||
a `detect` array on each node. AgentHub ships no template, so no rule fires
|
||||
until you install one.
|
||||
|
||||
| Node | Detect rules |
|
||||
|---|---|
|
||||
| `intake` | `Agent` matching subagent `Explore` — a lane sitting at nothing else yet, but already exploring, shows *something* instead of idle; `Skill` matching `brainstorming`, which is intake's own declared command |
|
||||
| `plan` | `Skill` matching `writing-plans`; `Write` matching `docs/.*plan.*\.md`; `Agent` matching `Plan\|Software Architect\|Backend Architect\|UX Architect`; `TaskCreate` (any) |
|
||||
| `implement` | `Skill` matching `executing-plans\|subagent-driven-development\|test-driven-development`; `Edit` (any); `Write` to any path NOT under a `docs/` directory (`^(?!.*(?:^\|/)docs/)`) |
|
||||
| `tests` | `Bash` matching `\b(npm (run )?test\|pytest\|vitest\|jest\|go test\|cargo test\|node\s+--test\|rspec\|phpunit\|dotnet test\|mvn test\|gradle test)\b`; `Agent` matching `Test Results Analyzer\|API Tester\|Performance Benchmarker` |
|
||||
| `review` | `Skill` matching `code-review\|requesting-code-review\|receiving-code-review`; `Bash` matching `\bgh\b[^;&\|]*\bpr diff\b`; `Agent` matching a name containing `reviewer`/`Reviewer` (excluding `senior-gate-reviewer`, a lookbehind), `Evidence Collector`, or `Accessibility Auditor` |
|
||||
| `gate` | `Skill` matching `verification-before-completion`; `Agent` matching `senior-gate-reviewer\|Reality Checker` — entering the gate, never its verdict |
|
||||
| `ship` | `Bash` matching a `git push` **invocation** — `git`, then option groups, then `push` as the first non-flag token — or `gh … pr create` |
|
||||
| `done` | none — see below |
|
||||
A few lessons from rules that ran against real sessions, worth keeping when you
|
||||
write your own:
|
||||
|
||||
**`git diff` is deliberately NOT a review signal.** It was, and measured on a
|
||||
real session four `git diff --stat` runs — used to verify a generated file —
|
||||
outranked 81 test runs and 30 edits and pinned the lane at `review`, because
|
||||
`detect()` takes the LAST matching node and `recordDetection` is forward-only.
|
||||
Reading a diff is constant background activity, so it carries no stage
|
||||
information. `gh pr diff` stays: that one really is a review act.
|
||||
- **Detection is a safety net, not the mechanism.** Because `recordDetection`
|
||||
is forward-only AND never overrides a higher declared stage, a fix-loop
|
||||
re-entry that drops back a node is invisible to detection — only an
|
||||
`agenthub stage` declaration moves the lane back down. What detection adds is
|
||||
the stage an agent forgot to declare and the heartbeat that keeps a working
|
||||
lane from reading STALLED.
|
||||
- **Give a terminal node (`done`, `merged`) no rule.** Inference must never
|
||||
reach a terminal state on its own; that would let a heuristic assert an
|
||||
outcome instead of observing activity. A rule on a gate node should detect
|
||||
that the gate *activity* started, never its verdict.
|
||||
- **`git diff` is not a review signal.** Measured on a real session, four
|
||||
`git diff --stat` runs — used to verify a generated file — outranked 81 test
|
||||
runs and 30 edits and pinned the lane at `review`, because `detect()` takes
|
||||
the LAST matching node and detection is forward-only. Reading a diff is
|
||||
constant background activity; `gh pr diff` really is a review act.
|
||||
- **Match a `git push` *invocation*, not the word `push`.** `git[^;&|]*push`
|
||||
misses `git -c credential.helper='!f() { … }; f' push` (a `;` inside a quoted
|
||||
value), and `git[\s\S]*push` over-matches `git log --oneline -1 && echo "… push …"`.
|
||||
Consume option groups explicitly, treating a quoted option value as one unit,
|
||||
then require `push` as the first non-flag token — that accepts `git push` and
|
||||
`git -c core.hooksPath=/dev/null push` while rejecting `git commit -m "don't push"`,
|
||||
`docker push` and `npm run push-docs`.
|
||||
|
||||
**The `ship` pattern matches a git *invocation*, not the word `push`.** Two
|
||||
weaker forms were tried and both failed against real commands:
|
||||
|
||||
- `git[^;&|]*push` (same shell segment) **misses** this repo's own push, because
|
||||
`git -c credential.helper='!f() { … }; f' push` puts a `;` between the two
|
||||
inside a quoted value. Six real pushes went undetected that way.
|
||||
- `git[\s\S]*push` (anywhere after `git`) **over-matches**, and did so on a live
|
||||
lane within minutes: `git log --oneline -1 && echo "… push …"` pinned the lane
|
||||
at `ship`. Since `ship` sits near the end of the pipeline and detection is
|
||||
forward-only, that single over-read stuck — the same failure `git diff` caused
|
||||
for `review`.
|
||||
|
||||
So the pattern consumes option groups explicitly, treating a quoted option value
|
||||
as one unit, and then requires `push` to be the first non-flag token. That
|
||||
accepts `git push`, `git -c core.hooksPath=/dev/null push` and the credential
|
||||
-helper form, while rejecting `git log … "push"`, `git commit -m "don't push"`,
|
||||
`docker push`, and `npm run push-docs`.
|
||||
|
||||
The `ship-feature` template (`server/data/pipelines/ship-feature.json`) carries
|
||||
its own rules, aimed at the commands and Superpowers skills its driving skill
|
||||
actually runs:
|
||||
|
||||
| Node | Detect rules |
|
||||
|---|---|
|
||||
| `intake` | `Skill` matching `brainstorming`; `Bash` matching `agenthub … feature activate`; `Agent` matching subagent `Explore` |
|
||||
| `plan` | `Skill` matching `writing-plans`; `Write` matching `docs/superpowers/specs/lane-.*\.md`; `Agent` matching `Plan\|Software Architect`; `TaskCreate` (any) |
|
||||
| `implementing` | `Skill` matching `test-driven-development\|executing-plans\|subagent-driven-development\|systematic-debugging`; `Edit`/`Write` to any path NOT under `docs/superpowers/specs/` (resp. not under `docs/`) |
|
||||
| `gates` | `Bash` matching `agenthub … hook ci-gate` or `agenthub … sync-base --check` |
|
||||
| `e2e-feature` | `Bash` matching `agenthub … up --qc` or `agenthub … hook e2e` |
|
||||
| `review` | `Skill` matching `code-review\|requesting-code-review\|receiving-code-review`; `Agent` matching a name containing `reviewer`/`Reviewer` (excluding `senior-gate-reviewer`, a lookbehind) |
|
||||
| `qc` | `Agent` matching `qc-local\|Evidence Collector\|Accessibility Auditor`; `Bash` matching `agenthub … lanes proof-link` |
|
||||
| `gate` | `Agent` matching `senior-gate-reviewer\|Reality Checker`; `Skill` matching `verification-before-completion` |
|
||||
| `publishing` | `Skill` matching `finishing-a-development-branch`; `Bash` matching a `git push` **invocation** (the same pattern `default.json`'s `ship` uses) |
|
||||
| `pr-open` | `Bash` matching `gh … pr create` |
|
||||
| `watching-pr` | `Bash` matching `gh … pr view` |
|
||||
| `e2e-feature-passed`, `qc-plan`, `reported`, `merged`, `done` | none — declaration-only |
|
||||
|
||||
Two rules are deliberately absent from this template. There is **no `git diff`
|
||||
rule on `review`**, for the reason the `default` template learned the hard way
|
||||
below; the `code-review` skill invocation is the honest signal. And `merged` /
|
||||
`done` carry no rule at all, because inference must never reach a terminal
|
||||
state — a test pins that.
|
||||
|
||||
Detection here is a **safety net, not the mechanism**: the `ship-feature-lane`
|
||||
skill declares every one of these stages with `agenthub stage` itself. What
|
||||
detection adds is the stage an agent forgot after a context compaction, the
|
||||
heartbeat that keeps a working lane from reading STALLED, and coverage for a
|
||||
lane running Superpowers skills without the driving skill at all. It cannot
|
||||
substitute for the skill's own declarations: because `recordDetection` is
|
||||
forward-only AND never overrides a higher declared stage, a fix-loop re-entry
|
||||
that drops back to `gates` is invisible to detection — only the skill's
|
||||
`agenthub stage gates` moves the lane back down.
|
||||
|
||||
`done` in `default.json` deliberately has no rule — it is explicitly out of
|
||||
scope for inference (see the task's "Out of scope" list): `done` is the one
|
||||
state detection must never reach on its own, since inferring it would let a
|
||||
heuristic assert an outcome instead of observing activity. `intake` and `gate`
|
||||
now carry entry-activity rules (an `Explore` agent, a `senior-gate-reviewer`/
|
||||
`Reality Checker` agent or `verification-before-completion` skill) — these
|
||||
detect that the *activity* of exploring or gate-reviewing started, never a
|
||||
gate's pass/fail verdict, so the "no outcome inference" rule still holds.
|
||||
|
||||
To add a rule, drop a template into the directory named by the
|
||||
To add a rule, put a template in the directory named by the
|
||||
`DASHBOARD_PIPELINES_DIR` environment variable (see "Custom pipeline
|
||||
templates" above) with a `detect` array on the node you want to match:
|
||||
|
||||
@@ -1415,8 +1303,8 @@ Detection never rewinds a lane and never fights a declaration:
|
||||
- **Declared beats detected.** A detection is written only when the lane's
|
||||
*declared* stage index is strictly less than the detection's. A lane
|
||||
already declared at `review` ignores an `implement` detection outright.
|
||||
**One exception: a declaration on the pipeline's LAST node** (`done`, in both
|
||||
built-in templates) stops vetoing. The pipeline is finished there, so a later
|
||||
**One exception: a declaration on the pipeline's LAST node** (typically
|
||||
`done`) stops vetoing. The pipeline is finished there, so a later
|
||||
detection is new work rather than a stale rerun of what the claim covers —
|
||||
without the exception a lane left at `done` rejects every detection for the
|
||||
rest of its life, which reads as "auto-detect is broken". The exception
|
||||
@@ -1637,51 +1525,9 @@ The lane card shows a lock badge when the lane **holds** a named lock (its `lane
|
||||
- **Never delete the lock directory by hand.** A lock that outlives its holder (e.g. after a hard reboot) is cleaned up automatically — the next `acquire` on that name finds it stale (older than `LOCK_MAX_HOLD`) and breaks it. Deleting `LANES_ROOT/.locks/<name>/` yourself only races whoever else's `acquire` is about to do the same check safely.
|
||||
- **Never shrink `LOCK_MAX_HOLD` to force through a wait.** The 300-second floor exists specifically to stop this mistake. If the true holder is gone, the 300-second minimum wait is the price of safety. If the true holder is still running (e.g., a build with a network stall), shortening the timeout from 2700 to 300 does not help — it converts a slow success into a premature timeout, leaving the lock held and every other lane blocked forever.
|
||||
|
||||
## The ship-feature-lane skill (E1)
|
||||
## Lane tooling: QC boot, sync-base, MCP, integrations, CI
|
||||
|
||||
The **ship-feature-lane** skill is the ported Shipyard driving skill for autonomous end-to-end feature pipelines in a single AgentHub lane. It coordinates the entire development flow: implementation (TDD) → independent unit tests → pre-push CI gates + dev preflight → e2e testing → code review → local QC → senior gate authorization → PR push and publication → CI watch → final report.
|
||||
|
||||
**Every stage whose job is to judge the work runs in a fresh-context subagent, never in the driving session.** The plan debate (Stage 0b), unit tests (Stage 1b), the e2e run and its triage (Stage 3), code review (Stage 4), browser QC (Stage 6), the senior gate (Stage 7), and a re-triage of CI that stayed red after a fix (Stage 10) are each dispatched with the Agent tool to a subagent that starts with an empty context and receives only artifacts it can verify itself — working directory, lane spec, diff range, commands, and the other agents' reports verbatim, never the driving session's paraphrase of why a finding was fine. The stages the driving session keeps are the ones that *do* rather than judge: intake, implementation, the mechanical gates, publishing, the CI/PR watch, and applying every fix the judges ask for. `subagent_type: fork` is explicitly forbidden for these: a fork inherits the implementing session's context, so it inherits the belief that the code is already correct. The driving session implements and applies fixes; it never grades its own work. Stages 1b/3 use the generic `general-purpose` type (nothing to install). Stage 4 uses the generic `Code Reviewer` type as its catch-all, plus — in parallel, routed by which of `server/`, `client/` the diff touches — the plugin-qualified `agenthub:backend-reviewer` / `agenthub:frontend-reviewer` domain reviewers (nothing to install; `agenthub:` addressing works from any lane). Stages 6/7 use the lane-installed `qc-local`/`senior-gate-reviewer` agents.
|
||||
|
||||
### Getting the skill (nothing to do, if you run the plugin)
|
||||
|
||||
The skill lives in `skills/ship-feature-lane/`, and the repo root **is** the `agenthub` plugin root (`"source": "./"`), so `skills/` is the plugin's own skills directory. Anyone with the `agenthub` plugin installed already has it in every session, from any lane's working directory, as **`/agenthub:ship-feature-lane`** — with the bare `/ship-feature-lane` working as an alias unless another skill or command claims that name.
|
||||
|
||||
Only a plain checkout without the plugin needs the fallback:
|
||||
|
||||
```bash
|
||||
agenthub skills install
|
||||
```
|
||||
|
||||
Copies `skills/ship-feature-lane/` into `~/.claude/skills/ship-feature-lane/` (overwrites on reinstall — run it again after pulling an update to the skill), which restores the bare `/ship-feature-lane` name. One-time per machine, same as `agenthub lanes agents install` is one-time per lane.
|
||||
|
||||
**Agents come from the same plugin, under two names.** `qc-local` and `senior-gate-reviewer` live in `agents/` at the repo root, so the plugin exposes them as `agenthub:qc-local` / `agenthub:senior-gate-reviewer`. `agenthub lanes agents install` copies those same files into the lane's own `.claude/agents/` — the highest-priority agent source — which is what makes the bare `subagent_type: qc-local` the skill prefers resolve inside the lane. Either name reaches the same definition; the skill falls back to the `agenthub:` form when the lane copy is missing.
|
||||
|
||||
### Invoking the skill
|
||||
|
||||
```bash
|
||||
# Inside a lane's working directory:
|
||||
/ship-feature-lane <requirement>
|
||||
```
|
||||
|
||||
The skill frontloads all clarifying questions once, then runs unattended. The only interactive touchpoints are **Stage 0 (Q&A intake)** and **merging the PR on GitHub**; everything else runs to completion or escalates with a `blocked` status visible on the dashboard. Progress is declared via `agenthub stage` commands at every step.
|
||||
|
||||
### Current status: stages 0–8, 10–12, 14 work; stage 9 deferred, stage 13 partially wired (CI-wait works, dev-QC doesn't); stages 6–7 agent-gated
|
||||
|
||||
**Stages implemented now:**
|
||||
- **0-8**: Intake & frontloaded Q&A → plan → implementation → independent unit tests (1b) → gates+preflight → e2e on feature branch → code review → QC plan → QC → senior gate → publish PR
|
||||
- **10-12**: CI watch on the PR → report → watch PR until merge (gated fix-loop re-entries on review feedback)
|
||||
- **14**: Done
|
||||
|
||||
**Stages currently hardcoded skipped** (pending F's integrations):
|
||||
- **9 (Ticket)**: Ticket-filing integration not yet built — this stage is skipped entirely.
|
||||
|
||||
**Partially wired:**
|
||||
- **13 (Post-merge verification)**: CI-wait half now runs for real via `agenthub lanes ci status`. The dev-QC half (QCing the deployed site) is still unavailable — Stage 13 always ends noting that gap even when CI-wait passes clean.
|
||||
|
||||
**Stages 6 and 7 are now unblocked** — run `agenthub lanes agents install` once per lane (see "Installing the QC/gate agents" below) before a lane's first run through Stage 6. A lane that reaches Stage 6/7 without having installed the agents will fail to find the `qc-local`/`senior-gate-reviewer` subagent type; install and re-run.
|
||||
|
||||
See [the skill text](../skills/ship-feature-lane/SKILL.md) for the full pipeline definition.
|
||||
The commands below are primitives a driving session (or you) calls explicitly — AgentHub never sequences them.
|
||||
|
||||
### QC boot flag and profile integration
|
||||
|
||||
@@ -1704,7 +1550,7 @@ These environment variables are passed to the profile's `boot` hook during `agen
|
||||
|
||||
### Dev preflight and merge safety: sync-base
|
||||
|
||||
`agenthub lanes sync-base` is the ONE sanctioned merge in the pipeline — `origin/development` into a feature branch — used at Stages 2, 8, and 12. Three modes:
|
||||
`agenthub lanes sync-base` is the ONE sanctioned merge a session runs from the CLI — `origin/development` into a feature branch. Three modes:
|
||||
|
||||
```bash
|
||||
agenthub lanes sync-base --check feat/<slug> # read-only preflight: fetch + collision check + DEV_DELTA/DEV_OVERLAP
|
||||
@@ -1724,27 +1570,9 @@ GENERATED_MERGE_PATHS="api/openapi.json api/client.ts"
|
||||
|
||||
`MIGRATIONS_DIR` enables the collision preflight against a numbered-migrations directory. `GENERATED_MERGE_PATHS` gives the listed files a keep-ours merge driver (never hand-merged) and folds the profile's `regen` hook output into the sync commit — the single most common cross-lane conflict, for a repo that generates an API contract/client.
|
||||
|
||||
### Installing the QC/gate agents: agents install
|
||||
|
||||
Stage 6 (browser QC) and Stage 7 (senior GO/NO-GO gate) run as subagents — `qc-local` and `senior-gate-reviewer`. The plugin already ships them as `agenthub:qc-local` / `agenthub:senior-gate-reviewer`; installing them into the lane's own `.claude/agents/` is what makes the bare names work there:
|
||||
|
||||
```bash
|
||||
agenthub lanes agents install
|
||||
```
|
||||
|
||||
Writes both agent definitions (from the repo's own `agents/`, the same files the plugin ships) into `<lane>/.claude/agents/` and adds that directory to the lane's local `.git/info/exclude` (never the tracked `.gitignore` — this is a per-clone runtime concern, not an app-repo change). Idempotent and never automatic, the same shape as `agenthub lanes proof-link` — a session installs the agents explicitly, once, before a lane's first run through Stage 6/7 (or after a AgentHub upgrade ships updated templates — reinstalling overwrites, it doesn't merge).
|
||||
|
||||
**On a `codex` lane the same command installs nothing** (`installed: []`, and
|
||||
the Tools menu hides it): Codex never reads `.claude/agents/`, and codex lanes
|
||||
carry no pipeline to drive.
|
||||
|
||||
**Credentials are not embedded.** Unlike Shipyard's original per-lane agent generation, these templates carry no QA account credentials — this repo has no seeded-QA-account system yet. `qc-local` degrades gracefully: if it hits a login page with no credentials block present, it reports `LOCAL-QC: FAIL — login required, no seed-account mechanism configured for this profile` rather than guessing.
|
||||
|
||||
**`ticketer`, `dev-qc`, and `pr-reviewer`** are not ported yet — `ticketer` is invoked by Stage 9 (skipped), and `dev-qc` by Stage 13's dev-QC half (unavailable); `pr-reviewer` isn't referenced anywhere in this skill's text.
|
||||
|
||||
### Syncing MCP servers: mcp sync
|
||||
|
||||
A lane needs the same MCP servers (Playwright, a local-QC server) as its source repo to run Stage 3/6. `agenthub lanes mcp sync` gives it those:
|
||||
A lane needs the same MCP servers (Playwright, a local-QC server) as its source repo to run e2e or browser QC. `agenthub lanes mcp sync` gives it those:
|
||||
|
||||
```bash
|
||||
agenthub lanes mcp sync
|
||||
@@ -1766,11 +1594,11 @@ A profile can declare `.agenthub/profile/integrations.env` with `TRACKER_ENABLED
|
||||
agenthub lanes integration tracker # exit 0 = on, 1 = off
|
||||
```
|
||||
|
||||
This only reports what the PROFILE wants — it doesn't file a ticket, run dev-QC, or wait on CI. The agent that would act on an enabled `dev_qc` toggle isn't built yet, and neither is a `tracker` ticketer — `ship-feature-lane`'s Stage 9 stays skipped and Stage 13's dev-QC half stays unavailable regardless of what those two toggles read. `ci_wait`'s consumer is `agenthub lanes ci status` (added — see the command reference below); Stage 13's CI-wait half now honors it for real. The CLI command and `GET /api/lanes/:id/integrations/:name` are the only readouts: the lane card used to carry a row of three badges for these, and it no longer does — two of the three could never be anything but decoration, and a badge that reports a toggle nothing acts on costs a row of card height to say nothing.
|
||||
This only reports what the PROFILE wants — it doesn't file a ticket, run dev-QC, or wait on CI. Nothing in AgentHub acts on an enabled `tracker` or `dev_qc` toggle. `ci_wait`'s consumer is `agenthub lanes ci status` (below). The CLI command and `GET /api/lanes/:id/integrations/:name` are the only readouts: the lane card used to carry a row of three badges for these, and it no longer does — two of the three could never be anything but decoration, and a badge that reports a toggle nothing acts on costs a row of card height to say nothing.
|
||||
|
||||
### Post-merge CI status: agenthub lanes ci status
|
||||
|
||||
Check GitHub Actions status for a commit SHA or branch ref — used by Stage 13's CI-wait loop:
|
||||
Check GitHub Actions status for a commit SHA or branch ref — one shot, so a session waiting on CI polls it:
|
||||
|
||||
```bash
|
||||
agenthub lanes ci status <branch-or-sha> [<id>]
|
||||
@@ -1778,87 +1606,6 @@ agenthub lanes ci status <branch-or-sha> [<id>]
|
||||
|
||||
Exit codes: `0` = all checks passed, `1` = at least one check failed, `3` = checks pending or in progress. The `<id>` parameter is optional and filters results to a specific lane if provided; if omitted, it's not needed.
|
||||
|
||||
### Pipeline template: ship-feature (16 node stages)
|
||||
|
||||
The skill uses the `ship-feature` pipeline template, which defines the following 16 stages (node IDs):
|
||||
|
||||
1. **intake** — frontloaded Q&A and feature activation
|
||||
2. **plan** — investigation and plan debate
|
||||
3. **implementing** — TDD implementation
|
||||
4. **gates** — CI gates + preflight check
|
||||
5. **e2e-feature** — run e2e on feature branch
|
||||
6. **e2e-feature-passed** — e2e result gate
|
||||
7. **review** — code review
|
||||
8. **qc-plan** — bound QC scope
|
||||
9. **qc** — browser QC (agent-gated until qc-local lands)
|
||||
10. **gate** — senior GO/NO-GO gate (agent-gated until senior-gate-reviewer lands)
|
||||
11. **publishing** — push branch + open/update PR
|
||||
12. **pr-open** — PR open and published
|
||||
13. **reported** — report posted
|
||||
14. **watching-pr** — watch for comments, conflicts, merge
|
||||
15. **merged** — PR merged, post-verify transition
|
||||
16. **done** — pipeline complete
|
||||
|
||||
The Workspace page (`/run`) displays this template with nodes rendered in five states: `failed` (rejected), `current` (now), `done` (with evidence), `passed-no-evidence` (claimed or skipped), and `pending` (not reached).
|
||||
|
||||
### More stage names than nodes
|
||||
|
||||
Each node carries aliases, and they do two different jobs. Some absorb a
|
||||
near-miss (`implement` for `implementing`). Others are **sub-states**: a name
|
||||
the skill declares to say *why* the lane is sitting on a node, without adding a
|
||||
node to the map.
|
||||
|
||||
This is the shape Shipyard converged on — its dashboard renders 13 nodes while
|
||||
its `PHASES` table folds roughly 35 stage names onto them. A pipeline map is
|
||||
read at a glance across many lanes at once, so it stays coarse; the stage name
|
||||
is read one lane at a time, so it can be specific.
|
||||
|
||||
| Node | Near-miss aliases | Sub-states the skill declares |
|
||||
|---|---|---|
|
||||
| `intake` | `assigned`, `claimed`, `start` | `bootstrapping` |
|
||||
| `plan` | `planning`, `brainstorm`, `design` | — |
|
||||
| `implementing` | `implement`, `coding`, `build` | — |
|
||||
| `gates` | `pre-push-gate`, `tests` | `migration-collision`, `sync-conflict` |
|
||||
| `e2e-feature` | `e2e`, `live` | `booting`, `e2e-scoped` |
|
||||
| `e2e-feature-passed` | `e2e-passed` | — |
|
||||
| `review` | `reviewing`, `code-review`, `self-review` | — |
|
||||
| `qc-plan` | — | — |
|
||||
| `qc` | — | — |
|
||||
| `gate` | `sr-gate`, `verify`, `verification` | `gate-blocked` |
|
||||
| `publishing` | `push` | — |
|
||||
| `pr-open` | `ship`, `pr`, `push-conflict` | `push-revalidate` |
|
||||
| `reported` | — | — |
|
||||
| `watching-pr` | — | `pr-comment-fix` |
|
||||
| `merged` | — | — |
|
||||
| `done` | `complete`, `completed` | — |
|
||||
|
||||
50 names over 16 nodes. Every one of them has a **source**: the skill declares
|
||||
it, or `default.json` uses it (an agent moving between pipelines will type
|
||||
what the other one taught it), or Shipyard's `PHASES` lists it. An alias with
|
||||
no source is not "flexibility" — it is a synonym someone imagined, and the
|
||||
`agenthub stage` warning already catches a name that resolves to nothing, which is
|
||||
better feedback than silently absorbing every plausible spelling. Twelve
|
||||
sourceless aliases were written and then cut for exactly this reason.
|
||||
|
||||
Two constraints on this:
|
||||
|
||||
- **No alias may collide** with another node's id or alias. `phaseIdx` takes the
|
||||
FIRST match, so a duplicate would silently resolve a declaration onto the
|
||||
wrong node. A test asserts the whole template is collision-free.
|
||||
- **Aliases of one node share one record slot.** `lane.stages` is keyed by the
|
||||
declared string, and `stageRecords` resolves each key onto its node — so
|
||||
declaring `migration-collision` and then `gates` leaves ONE record for that
|
||||
node (the canonical `gates` one wins). A sub-state that needs to keep its own
|
||||
`--evidence` separately has to be a real node, not an alias.
|
||||
|
||||
`integrate`, `dev-gates`, `e2e-on-dev` and `push-dev` are deliberately **not**
|
||||
nodes here. Shipyard had them and retired them on 2026-07-16 (see the comment
|
||||
above `PHASES` in its `dashboard/src/lib/constants.js`); it now folds those
|
||||
names into the surviving phases so old feature cards still render. AgentHub never
|
||||
shipped them, so there is nothing to fold.
|
||||
|
||||
These nodes also carry `detect` rules; see "Stage detection → Where the rules live".
|
||||
|
||||
## Orchestration: what AgentHub does NOT do
|
||||
|
||||
**AgentHub does not chain, queue, retry, or evaluate gates.**
|
||||
|
||||
Reference in New Issue
Block a user