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.
This commit is contained in:
2026-08-24 16:34:55 +07:00
parent 2cbb185b7f
commit 2103dde889
12 changed files with 212 additions and 0 deletions
+30
View File
@@ -434,6 +434,34 @@ ccam lanes pipeline ship-feature # switch it
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": "/ccam:ship-feature-lane"` on the 16-node `ship-feature` template, `"skill": "/ccam: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 `ccam lanes pipeline` step first:
```bash
cd ~/lanes/myrepo__feat-export
# 16 stages: ends in a published PR, watched until a human merges it
ccam ship-full "add an export button"
# → lane switched to `ship-feature`, then: claude "/ccam:ship-feature-lane add an export button"
# 8 stages: in-repo feature work, stops at review/gate — no PR, no browser QC
ccam ship-quick "fix the CSV separator"
# → lane switched to `default`, then: claude "/ccam:ship-feature fix the CSV separator"
ccam ship-full "add an export button" --print # show the command, switch nothing, run nothing
```
| | `ccam ship-full` | `ccam ship-quick` |
|---|---|---|
| Template | `ship-feature` (16 nodes) | `default` (8 nodes) |
| Skill | `/ccam:ship-feature-lane` | `/ccam: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.
**The template also decides what the Workspace run console pre-fills.** A template may name the slash command that drives it — `"skill": "/ccam:ship-feature-lane"` on the 16-node `ship-feature` template, `"skill": "/ccam: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 `ccam stage` fixes it.
## Attaching a real terminal to a lane
@@ -559,6 +587,7 @@ Create a JSON file in your custom pipeline directory (e.g., `~/.ccam/pipelines/m
{
"id": "ml-training",
"name": "ML Training Pipeline",
"skill": "/my-training-skill",
"nodes": [
{ "id": "data", "label": "data", "icon": "📊", "gate": false, "aliases": ["intake"] },
{ "id": "preprocess","label": "preprocess","icon": "⚙️", "gate": false, "aliases": ["prepare", "clean"] },
@@ -573,6 +602,7 @@ Create a JSON file in your custom pipeline directory (e.g., `~/.ccam/pipelines/m
- **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. `"/ccam: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.
- **nodes** (array, required): list of pipeline stages, in order
- **id** (string): stage identifier (used in `ccam stage`)
- **label** (string, optional): display name in the UI; defaults to `id` if omitted