docs(lanes): design E1 — ship-feature pipeline template + skill port

Scopes the first of E's four independent pieces: --qc boot flag +
QC_BOOT_ENV, the pipeline template, and the ported skill text. Agents,
sync-base, and F's integrations are named but deliberately out of scope,
per the roadmap's own subsystem split.
This commit is contained in:
2026-08-04 17:37:33 +07:00
parent 5d5ea32db6
commit b08504858c
@@ -0,0 +1,75 @@
# E1 — Ship-feature pipeline template + skill port
**Status:** approved 2026-08-04. Roadmap it belongs to:
`docs/superpowers/plans/2026-08-03-shipyard-parity-lanes.md` (subsystem **E**).
## Problem
Shipyard's `ship-feature/SKILL.md` (`~/MyDrive/Projects/ResearchAndDevelopment/AgentWorkflow/claude/skills/ship-feature/SKILL.md`, 208 lines, 16 stages) is the driving skill that runs a lane end-to-end: intake → TDD implement → gates → e2e → review → QC → senior gate → publish PR → watch → post-merge verify → done. CCAM has no equivalent yet. E (per the roadmap) is four independent pieces — this spec covers only the first: the pipeline template + the skill text itself. Agents (qc-local, senior-gate-reviewer, ticketer, dev-qc, pr-reviewer) and `ccam lanes sync-base` are separate, later tasks; F's integrations (tracker, dev-QC, CI-wait) are later still.
## Scope
**In scope:**
1. A backend gap the skill text requires to be truthful: `ccam lanes up --qc`, backed by a new `QC_BOOT_ENV` profile declaration.
2. `server/data/pipelines/ship-feature.json` — the node/stage definitions the dashboard renders against.
3. `.claude/skills/ship-feature-lane/SKILL.md` — the ported skill, every Shipyard `bin/…` call rewritten to its CCAM equivalent.
**Out of scope (later tasks, referenced by name in the ported skill but not built here):**
- `ccam lanes sync-base` (the migration-collision + merge-driver command Stage 2/8/12 call).
- `ccam lanes mcp sync` (F) — Stage 0's MCP preflight becomes a manual check + a message to the human, not an auto-sync call.
- `ccam lanes integration <name>` / `.ccam/profile/integrations.env` (F) — tracker/dev_qc/ci_wait are **hardcoded off** in this port; Stage 9 (ticket) and the dev-QC half of Stage 13 unconditionally skip; Stage 10's CI watch unconditionally uses the `gh pr checks` fallback the original skill already documents for "helper not configured" (no conditional branch needed — there is nothing to branch on yet).
- `ccam ci` (F, `ci-job.sh` port) — same reasoning; Stage 10/13's CI-triage logic that depends on it is dropped to the fallback path.
- The five agents — the skill references them by `subagent_type` at Stages 6/7/9/13/12(pr-reviewer callout), but none exist yet. This spec's own verification stops where an agent invocation would occur.
## Decisions
| | Choice | Rationale |
|---|---|---|
| `--qc` boot flag | New `QC_BOOT_ENV` profile declaration (space-separated `KEY=value` pairs, same shape as Shipyard's), injected into the boot hook's environment only when `up` is called with `qc: true` | The skill calls `lane-up.sh "$N" --qc` in Stage 3 to get a deterministic (mocked/stubbed) stack for QC. Without a real flag this line in the ported skill would be false documentation — violates this repo's docs rule (executable, aligned with actual scripts). A profile that never declares `QC_BOOT_ENV` gets no extra env (dead code doesn't run), same pattern every other A2 declaration in `DEFAULTS` already follows. |
| `state.sh "$N" init` (open a clean feature slot before Stage 0's Q&A) | Dropped — replaced by a plain `ccam feature activate <slug>` at Stage 1 | Task B's `activateFeature` already archives the outgoing active feature automatically on switch; Shipyard needed a separate `init` step because it had no DB-backed archive. Porting `init` as a distinct call would be redundant with what `activate` already does. |
| Integration toggles (tracker/dev_qc/ci_wait) | Hardcoded off, not a runtime check | F doesn't exist yet. The alternative (a stub `ccam lanes integration` that always returns off) adds surface area for no behavioral difference today; the skill's own text already documents the "toggle off" behavior for each stage, so hardcoding is truthful, not a shortcut. When F ships, this becomes a follow-up edit to swap hardcoded skips for real checks — an explicit, later, reviewable diff. |
| Hook names for `lane-ci-gate.sh` / `lane-e2e.sh` | `ccam lanes hook ci-gate` / `ccam lanes hook e2e` | Both names are **already** in `server/lib/lane-profile.js`'s `HOOKS` allowlist, added during A1 specifically "because... the driving skill needs that surface stable" — this spec is that driving skill. No backend change needed for these two. |
| `.harness-lane` marker / `lane-assign.sh` | Dropped entirely | CCAM already resolves a lane from `cwd` (path-boundary prefix match, documented in this repo's CLAUDE.md) — the marker file and the assign step exist in Shipyard only because it has no such resolution. |
| `@@HARNESS_ROOT@@` / `@@DEV_QC_MCP@@` / `@@TRACKER_MCP@@` placeholders | Dropped | Install-time templating that doesn't exist in CCAM; every reference becomes the literal CCAM command or, for the two MCP placeholders, is deleted along with the (out-of-scope) integrations that used them. |
## Command mapping
| Shipyard | CCAM | Status |
|---|---|---|
| `state.sh "$N" set stage=X status=Y` | `ccam stage X --status Y` | exists |
| `state.sh "$N" activate feat/<slug>` | `ccam feature activate <slug>` | exists (Task B) |
| `state.sh "$N" init` | (dropped — see Decisions) | n/a |
| `lane-ci-gate.sh "$N"` | `ccam lanes hook ci-gate` | exists (hook allowlisted) |
| `lane-up.sh "$N" --qc` | `ccam lanes up --qc` | **new in this task** |
| `lane-e2e.sh "$N" [-- specs]` | `ccam lanes hook e2e [-- specs]` | exists (hook allowlisted) |
| `lane-sync-dev.sh …` | `ccam lanes sync-base …` | later task (referenced, not built) |
| `lane-mcp-sync.sh "$N"` | `ccam lanes mcp sync` | F (referenced as manual fallback) |
| `lane-env.sh "$N" --check X` | (hardcoded off) | F (deferred) |
| `ci-job.sh status\|failures\|rerun\|cancel` | `ccam ci …` | F (deferred; skill uses its own `gh`-only fallback) |
## Design
### `--qc` boot flag
- `QC_BOOT_ENV` added to `server/lib/lane-profile.js`'s `DEFAULTS` (empty string default — off).
- `POST /:id/up` accepts `{build, qc}` in its body (currently only reads `build`); `qc: true` is threaded through to whatever assembles the boot hook's environment (read the actual `upLane`/hook-invocation code path in `server/lib/lane-runtime.js` before implementing — this spec names the profile declaration and the route contract, not the exact internal plumbing function).
- `ccam lanes up [--no-build] [--qc]` in `bin/ccam.js``--qc` sets `{qc: true}` in the POST body alongside the existing `--no-build``{build: false}` handling.
- `QC_BOOT_ENV` format: space-separated `KEY=value` pairs, parsed the same way this file already parses `PORTS`/`LANE_DIRS` (space-separated lists) — not a new parsing convention.
### Pipeline template
`server/data/pipelines/ship-feature.json` — one node per skill stage (0 through 15, using the skill's own stage names: `intake`, `plan`, `implementing`, `gates`, `e2e-feature`, `e2e-feature-passed`, `review`, `qc-plan`, `qc`, `gate`, `publishing`, `pr-open`, `reported`, `watching-pr`, `merged`, `done`), same node-definition shape `default.json` already uses (label, `detect` rules matching the `stage=` values `ccam stage` sets). `default.json` untouched. A lane opts into this template via its `pipeline` field, same mechanism every other custom template already uses.
### Skill
`.claude/skills/ship-feature-lane/SKILL.md` — the full port. Frontmatter, hard rules, and all 16 stages carried over with the Command mapping table's substitutions applied line-by-line. Every hard-won rule (turn-liveness, no-retry-cap/phase-clock, one-driver-per-MCP, never-push-base, the fix-loop's two fast-path exceptions, cross-lane etiquette) is kept verbatim in spirit, with `lane-lock.sh` etiquette re-pointed at D's `ccam lock` CLI and named locks.
## Invariants
1. **CCAM still does not orchestrate.** The skill runs in the session and calls `ccam` commands one at a time; nothing in this task adds chaining, gate evaluation, or retry logic to the dashboard itself.
2. **Every command the skill text names must actually exist and behave as documented** — the docs-markdown rule (executable examples, aligned with actual scripts) applies to a skill file exactly as it applies to `docs/*.md`.
3. **A profile that never declares `QC_BOOT_ENV` sees no behavior change**`--qc` degrades to a plain boot when the declaration is absent, same as every other optional A2 declaration.
## Verify
A real dry run on a scratch repo with a profile, walking Stage 0 through Stage 2 (intake → plan → `ccam lanes hook ci-gate`) — the furthest the pipeline can go without the not-yet-built agents. Confirms every `ccam` command named in stages 02 exists and behaves as documented. `ccam lanes up --qc` gets its own unit/integration test verifying `QC_BOOT_ENV` reaches the boot hook's environment when declared, and is a no-op when absent. Full Stage 0→14 verification is the agents-port task's responsibility, once qc-local/senior-gate-reviewer exist.