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.
8.7 KiB
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:
- A backend gap the skill text requires to be truthful:
ccam lanes up --qc, backed by a newQC_BOOT_ENVprofile declaration. server/data/pipelines/ship-feature.json— the node/stage definitions the dashboard renders against..claude/skills/ship-feature-lane/SKILL.md— the ported skill, every Shipyardbin/…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 thegh pr checksfallback 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.shport) — 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_typeat 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_ENVadded toserver/lib/lane-profile.js'sDEFAULTS(empty string default — off).POST /:id/upaccepts{build, qc}in its body (currently only readsbuild);qc: trueis threaded through to whatever assembles the boot hook's environment (read the actualupLane/hook-invocation code path inserver/lib/lane-runtime.jsbefore implementing — this spec names the profile declaration and the route contract, not the exact internal plumbing function).ccam lanes up [--no-build] [--qc]inbin/ccam.js—--qcsets{qc: true}in the POST body alongside the existing--no-build→{build: false}handling.QC_BOOT_ENVformat: space-separatedKEY=valuepairs, parsed the same way this file already parsesPORTS/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
- CCAM still does not orchestrate. The skill runs in the session and calls
ccamcommands one at a time; nothing in this task adds chaining, gate evaluation, or retry logic to the dashboard itself. - 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. - A profile that never declares
QC_BOOT_ENVsees no behavior change —--qcdegrades 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 0–2 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.