Move .claude/agents/ and server/data/agent-templates/ship-feature-lane/ into one agents/ at the repo root — which IS the plugin root, so all five agents now ship with the ccam plugin as ccam:<name>, the same way skills already do. The two pipeline agents were byte-identical copies in both places; that duplication is gone. `ccam lanes agents install` now reads them straight from agents/, so the per-lane copy in <lane>/.claude/agents/ still gives the bare `subagent_type: qc-local` the ship-feature-lane skill prefers. Skill and docs record both names: bare inside a lane, ccam: as fallback.
40 KiB
name, description
| name | description |
|---|---|
| ship-feature-lane | Autonomous end-to-end feature pipeline for ONE CCAM lane. Invoke inside a lane's working directory with a requirement: `/ship-feature-lane <requirement>`. Frontloads ALL clarifying questions once, then runs unattended: implement (TDD) → independent unit-test agent → pre-push CI gates + dev preflight → e2e on the feature branch (agent-run) → code review (fresh-context agent) → local QC → senior GO/NO-GO gate → push branch + open PR (base `development`) → CI watch → report, then watches the PR (new comments → gated fix-loop; base conflicts → sync development into the branch) until a HUMAN merges it. The PR is only published after all local gates pass and the senior gate says GO — it's finalized when reviewers see it. Declares lane stage at every step via `ccam stage` for the dashboard. Use when the user wants to build/ship/implement a feature in a CCAM lane. NOTE: ticket-filing and post-merge dev-site QC are currently OFF (F's integrations aren't built yet) — this pipeline stops at Stage 14 once dev CI/dev-QC support lands. |
Ship Feature Lane (CCAM lane pipeline)
You are running the autonomous feature pipeline for one CCAM lane. The human's only interactive touchpoints are Stage 0 (frontloaded Q&A) and merging the PR on GitHub; everything else runs to completion or to a blocked escalation, reporting progress through ccam stage (which the dashboard renders).
Setup — do this first, every run
LANE_DIR="$(pwd)" # the lane clone IS your cwd — CCAM resolves the lane from this, never a hardcoded path
- CCAM resolves your lane from
cwdautomatically (longest path-boundary prefix match) — there is no marker file to check and no separate assign step. Ifccam stageorccam feature activateever fails with "no lane found", you are not inside a lane's working directory; stop and tell the human. - Pipeline template is this skill's contract, not whatever the lane started on. Before your first
ccam stagecall, runccam lanes pipeline ship-feature(no id needed — resolves fromcwdlikeccam stage; idempotent, a no-op if already set) so every stage below resolves against the 16-nodeship-featuremap. The human is never expected to pick this in Add Lane or anywhere else — you force it to match the skill actually running. - All stage updates go through
ccam stage <stage> [--status <s>] [--evidence "..."]— call it at the start of every stage (this is also the heartbeat, visible on the dashboard). - Integration toggles: check, don't assume.
ccam lanes integration tracker,ccam lanes integration dev_qc,ccam lanes integration ci_waiteach exit 0 (on) or 1 (off), reading the profile'sintegrations.env. No agent exists yet to actually FILE a ticket or run dev-QC even when a toggle reads on (ticketer/dev-qcare a later task) — so regardless of the check's result, Stage 9 (ticket) stays skipped, Stage 13's dev-QC and dev-CI-wait halves stay skipped, and Stage 10's CI watch keeps using the plaingh pr checkspath (ccam cidoesn't exist yet). Check the toggle where noted below anyway, so the evidence you record is honest about whether the PROFILE wants the integration on, distinct from whether CCAM can act on it yet. - Heartbeat during long stages. Implementing (Stage 1), CI waits (Stage 10), and the watch/post-merge polls (Stages 12–13) can run many minutes between stage transitions — bump the heartbeat with
ccam stage <same-stage>after each commit and on each poll iteration, so the dashboard doesn't false-flag a working lane as stalled. - Profile hooks (
bootstrap/boot/migrate/seed/ci-gate/e2e/health/regen) run throughccam lanes hook <name> [args…]andccam lanes up/down. Use them; don't reinvent their logic. - NEVER merge or rebase branches manually. The ONLY merge that ever happens in this flow is
origin/developmentINTO the feature branch, and only throughccam lanes sync-base(fetches fresh, pre-checks migration collisions, auto-regenerates generated files — see the note on this command in Stage 2; it is a LATER task, referenced here by its intended contract). There is no other direction: never merge a feature branch into anything locally, never commit ondevelopment, and never touchmain. - NEVER push
origin/developmentororigin/main(HARD).developmentmoves ONLY when a human merges a PR on GitHub. The only branch you ever push is your ownfeat/<slug>— and only after the senior gate's GO (Stage 8). If you ever find yourself typinggit pushwithdevelopmentormainon the line: STOP,ccam stage <current> --status blocked.
Context recovery — after conversation compaction
Long pipelines outlive the context window. When context is compacted (summarized), re-derive these before continuing:
LANE_DIR="$(pwd)" # lane = cwd, resolved fresh
Then check your current position:
- Lane state:
ccam feature show <slug>(orccam lanesfor the lane's own row) — shows current stage, status, feature title, branch, gate decision, PR URL, notes. - Git branch:
git rev-parse --abbrev-ref HEAD— which branch you're on. - Feature slug: from the branch name (
feat/X→X), or fromccam feature list(the lane's currently-active feature is marked▶).
Resume from the stage shown. If state says stage=X status=running, you were mid-stage X when context compacted — re-run that stage from the top (all ccam commands are idempotent).
- MCP preflight (fail fast): if this is the lane's first run, or a required Playwright MCP's
browser_*tools aren't available in this session, runccam lanes mcp sync— it relocates the source repo's already-configured MCP servers into this lane's.mcp.json. If the source repo has none configured (ENOMCPCONFIG), tell the human to configure.mcp.jsonmanually for the source repo first. Either way, restart the session after syncing so the new config loads —playwrightand a local-QC MCP are always required for Stage 3/6. Catching this at Stage 0 costs a minute; catching it at Stage 13 strands a merged feature unverified. - Agents preflight: if this is the lane's first run through this pipeline, run
ccam lanes agents installonce — it writes theqc-localandsenior-gate-revieweragent definitions into the lane's own.claude/agents/(idempotent; re-running just overwrites with the current definitions). That copy is what makes the baresubagent_type: qc-localresolve inside the lane. If the install isn't possible (not a git repo, or the CLI is unavailable), theccamplugin ships the same two agents, addressed asccam:qc-local/ccam:senior-gate-reviewer— use those names instead. Only if NEITHER exists are Stages 6/7 unable to launch.
Hard rules
- Publish ONLY after the senior-gate-reviewer returns
VERDICT: GO. "Publish" = push the feature branch + open/update the PR (Stage 8). Nothing reviewer-visible exists before GO, and nothing else authorizes it. - Testing and reviewing run in a FRESH-CONTEXT agent — never in this session. The session that wrote the code is its worst judge: it already believes the code is correct and it remembers the reasoning the tests are supposed to challenge. Every stage whose job is to judge the work — Stage 0b (plan debate), Stage 1b (unit tests), Stage 3 (e2e run + triage), Stage 4 (code review), Stage 6 (browser QC), Stage 7 (senior gate) — is dispatched with the Agent tool to a subagent that starts with an EMPTY context and receives only artifacts it can verify itself: the lane's working directory, the lane spec file, the diff range, the commands to run, and the other agents' reports verbatim. The stages you keep are the ones that do rather than judge: intake, implementing, the mechanical gates (Stage 2), publishing (Stage 8), the CI/PR watch (Stages 10–12), and applying every fix the judges ask for. Rules: never
subagent_type: forkfor these (a fork inherits this session's context, which defeats the whole point); never "save a round-trip" by doing the pass yourself; never paste your own conclusions ("I already checked X", "this is safe because…") into the brief — hand facts and paths, not verdicts. You implement and you apply fixes; you do not grade your own work.- Launch them FOREGROUND (they gate the pipeline, and their completion re-invokes you, which satisfies the turn-liveness rule).
- Each returns ONE parseable final line (
UNIT-TESTS:/E2E:/REVIEW:/LOCAL-QC:/VERDICT:). Parse that line; never infer a pass from prose.
- The fix-loop: any failure in stages 2–7 (gates/preflight, e2e, review, QC, senior gate), any red PR CI that's genuinely yours (Stage 10), and any worth-fixing review comment (Stage 12) → fix on the feature branch, and re-run from Stage 2 through Stage 8 (gates+preflight → e2e → review → QC plan → QC → senior gate → publish/update PR), then the Stage 10 CI watch. Never skip a gate — the full process applies; no shortcuts because "it's just review feedback".
- Re-entry that changes non-test source re-runs Stage 1b first (fresh unit-test agent on the new diff), then Stage 2 onward. A re-entry that only touches test files skips 1b — the tests are already what changed.
- EXCEPTION — test-only re-entry (browser-QC fast-path). If the re-entry's change is ENTIRELY test files —
git diff --name-onlysince the last browser-QC'd commit matches only test paths — the app's runtime behavior/UI is unchanged from the last QC'd pass. Run this EXACT stage set, nothing else:- Always run: Stage 2 (gates + preflight), Stage 7 (senior gate), Stage 8 (publish/update PR), and the Stage 10 tail (CI watch).
- Run only if e2e spec files are among the changed tests: Stage 3 (boot + e2e on the feature branch). No e2e specs changed → skip it.
- Always SKIP (runtime UI unchanged): Stage 5 (QC plan), Stage 6 (qc-local). Record
--evidence "QC skipped: test-only change". - If the diff contains ANY non-test file → this fast-path does NOT apply; take the full path above. The first pass (not a re-entry) always runs full QC.
- EXCEPTION — localized re-entry (scoped-e2e fast-path). On a re-entry whose diff since the last fully-validated commit is SMALL and LOCALIZED — only files inside the feature's own surface, NO migrations, NO contract/generated files, NO shared fixtures/utilities, NO dependency changes — you may shrink Stage 3's e2e to a SCOPED run of the specs covering the touched surface:
ccam lanes hook e2e -- <spec files>(scoped runs still heartbeat + lock + time-bound like the full suite), and browser QC runs SCOPED to the affected QC-Plan scenarios (tell the qc agent exactly which scenario numbers). Know the trade-off: there is no dev-merged full suite anymore — post-merge dev CI + dev-QC (Stage 13, currently off) are normally the integration net; without them, a localized re-entry after this lands is a real gap until F ships. If in doubt whether the change is localized, it isn't — run the full path.
- Run long helpers so they can't be killed mid-flight or hang your turn.
ccam lanes hook ci-gate,ccam lanes up,ccam lanes hook e2elegitimately run 3–20+ minutes (builds, tests, Playwright, lock waits). NEVER invoke them with the default Bash timeout (2 min kills them mid-flight and strands the lane half-done): userun_in_background: trueand poll the output file until done, or settimeout: 600000for the shorter gates. If a helper does die mid-run, don't panic: every hook is idempotent — re-run the step (e.g. re-runccam lanes up --no-buildto revive a stack). - Waiting + polling NEVER use a foreground
sleep(the Bash tool blocks it) orScheduleWakeup(that's a/loop-only primitive — this pipeline is not a/loopsession, so it won't sustain your watch). To pace a poll loop or wait out a timer, background the wait: runsleep <secs>withrun_in_background: true— you're re-invoked when it exits, and re-invoked the moment a backgrounded Agent or helper finishes, so you never busy-poll for background work. If you ever can't sustain a wait/loop in this session, setccam stage <current> --note "<honest note>"and STOP — never narrate a watch or loop you are not actually running. - e2e: actively poll — never wait on the completion re-invoke alone. This binds whoever actually runs the hook — normally Stage 3's e2e agent, so pass this paragraph into its brief verbatim; you only follow it yourself for an ad-hoc run. A hung suite never fires the completion re-invoke, stranding the lane at
stage=e2e-feature. When runningccam lanes hook e2e: start itrun_in_background: trueAND background asleep 90beside it. Each wake — finished → parse PASS/FAIL; still running → read the e2e log tail (ccam lanes logs <id> e2e), bump the heartbeat, and re-backgroundsleep 90, UNLESS it's erroring or has run past ~22 min, in which case kill the e2e task and treat it as FAIL → re-enter Stage 2. - Turn-liveness: never let the pipeline die silently. A "stalled" lane usually died one of two ways: (a) a turn ended with NOTHING pending — no backgrounded wait, no running helper, no background agent — so nothing ever re-invoked the session; or (b) a transient API error (rate-limit, 529/overload, connection refused) killed the turn mid-stage. Rules: while the pipeline is anywhere between Stage 1 and Stage 14 (done), every turn you end MUST leave at least one re-invoker pending (a
run_in_backgroundhelper/sleep or a background agent) — check before ending the turn. And on ANY resume after an error or a human nudge ("continue"), do not ask questions: re-derive position from lane state (Context recovery above) and continue the stage. If you truly cannot leave a re-invoker, set--note "watch needs re-trigger: <what to do>"so the dashboard shows it honestly. - No retry cap — the phase clock is the signal. A failing gate/QC/CI/e2e just re-enters the loop (fix on the feature branch, re-run from Stage 2); there is NO automatic block after N attempts. The dashboard shows how long the lane has sat in its current stage, so the human can spot a stuck or endlessly-looping lane and step in. Reserve
--status blockedfor GENUINE blockers you cannot resolve (an ambiguous merge conflict, a hard/unrecoverable error). - Commit only on the feature branch. Never commit on
development/main. Stage only intended files (nevergit add .blindly — this repo collects stray build/QA artifacts). - Keep the lane's state truthful: on any stop, set an accurate
ccam stage <stage> --status <status> --note "<why>". - Quality bar (applies to every code change, including fix-loop re-entries and follow-up PRs). Tests are sharp and meaningful — each pins a real behavior/edge case (happy + negative + boundary), none trivial, redundant, or coverage-padding. Comments are minimal — only the non-obvious why, matching the surrounding density; never narrate the what. Investigate before fixing (root cause, not symptom — use systematic-debugging). Prefer reusing/extending existing code over duplicating it.
- One driver per MCP browser server. Each MCP server owns ONE browser; two agents driving the SAME server interleave clicks in one tab. The local-QC MCP → the qc-local agent (Stage 6) only; the general
playwrightMCP → the main session for ad-hoc checks only (never while qc-local runs). Parallel agents on DIFFERENT servers are safe by design; a second concurrent driver on the SAME server is never OK. - Cross-lane etiquette (locks + siblings). Lanes share one machine and one dev site. Waiting on a cross-lane serializer (
ccam lock acquire <name>, e.g. around a shared build/e2e step) is NORMAL — it heartbeats while it waits, so you won't look stalled. NEVER free a lock by killing another lane's session or processes, deleting the lock's directory by hand, or shrinkingLOCK_MAX_HOLD; a dead holder's lock auto-expires on its own. If a lock wait times out: re-try with a longer--timeout, or set--status blockedwith a note and report. Touch ONLY your own lane's clone, state, and locks you hold.
Stages
0 — Intake & frontloaded Q&A (the only interactive part)
Do NOT jump to code. Understand the requirement first.
- Restate + quick scan. Restate the requirement. Do a fast targeted scan of the relevant code (use the
Exploreagent for breadth; the brainstorming skill if the requirement is fuzzy) so your questions are grounded in what actually exists. - Frontloaded Q&A. Ask the human every clarifying question in ONE batch: acceptance criteria, scope / non-goals, UI/UX specifics, data shapes, edge cases, which existing flows it touches. Sibling-surface check (mandatory): if your scan shows the app has N parallel surfaces of the pattern the requirement touches (e.g. several foldered areas, several list pages sharing a component) and the requirement names fewer than N, explicitly ask "this exists in [all N places] — apply to all, or only [the named ones]?" A missed sibling here costs a full second pipeline pass when a reviewer catches it on the PR. Then activate a clean feature slot for this run:
ccam feature activate <slug> --title "<short title>"— Task B'sactivatearchives whatever feature was previously active on this lane automatically, so this run's dashboard state starts clean without a separate "init" step. Then mark intake:ccam stage intake --status running. Announce "Questions answered — going autonomous now." After this, don't ask the human anything unless you hit ablockedescalation.
0b — Investigate & plan (autonomous)
ccam stage plan— now design a real plan and have it independently challenged before you implement.- Investigate (autonomous, thorough). Read the actual code paths, models, existing tests, and conventions the feature touches —
Explore/general-purposesubagents for breadth, then read the key files yourself for depth. Pin down: integration points, data/migration needs, API/contract impact, reuse opportunities, and risks. Use systematic-debugging if the feature is a fix (root-cause first, no symptom patches). - Plan. Produce a concrete implementation plan (the writing-plans skill): approach, files to change, the test strategy (which behaviors/edge cases each test will pin), migration/contract impact, and how each acceptance criterion is met.
- Debate the plan (adversarial review) — fresh context, never
fork. Spawn a SEPARATE sub-agent (Agent tool —Planorgeneral-purpose, FOREGROUND) to critique the plan + investigation. Hand it the requirement, the Stage-0 answers and the plan as text plus the paths to read; do NOT hand it your reasoning for why the plan is right — that is exactly what it is there to test. Ask for: missed requirements, wrong assumptions, a simpler approach, unhandled edge cases, acceptance-criteria gaps. Apply the worthwhile critiques (use receiving-code-review judgment — verify each point, don't blindly accept or reject). Iterate once or twice until the plan holds up. - Write the Q&A answers and the agreed plan to a lane spec file
docs/superpowers/specs/lane-<slug>.md(gitignored, or add it to.gitignoreif this is the first one) — the acceptance contract the senior gate checks against.
1 — Implement (TDD, to the plan)
- Choose a single-segment slug for the feature — lowercase, hyphens, NO slashes. Cut the feature branch from development (the PR base):
git fetch origin && git checkout -b feat/<slug> origin/development. - If Stage 0 activated a placeholder slug different from the final chosen one, reconcile:
ccam feature activate <slug>— it echoes back the canonicalized slug it actually stored; use THAT for the branch and every later reference. - Implement the agreed plan with the test-driven-development skill: failing test → minimal code → green → commit. Frequent small commits.
- Tests must be sharp and meaningful. Each test pins a real behavior or edge case from the plan / acceptance criteria — cover the happy path, the negative/error path, and boundaries. NO trivial or redundant tests: don't assert constants or framework internals, don't re-test the same path twice, don't pad for coverage. A few precise tests that would actually catch a regression beat many shallow ones.
- Comment only when it earns its place. Match the surrounding code's comment density. Comment the non-obvious why (intent, invariants, gotchas, links to context) — never narrate the what the code already says. Delete redundant/boilerplate/restating comments rather than adding them.
- If your stack generates an API contract/client and the API changed, regenerate it (
ccam lanes hook regen) so the contract-check gate passes (stacks without a contract gate skip this). ccam stage implementing
1b — Independent unit tests (fresh-context agent)
Your TDD tests in Stage 1 were written by the same mind that wrote the code, so they inherit its blind spots. This stage buys a second, uncontaminated set.
ccam stage tests --status running(an alias ofgates— the map holds; the stage name says these minutes are the independent test pass).- Launch a fresh-context agent (Agent tool,
subagent_type: general-purpose, FOREGROUND — neverfork). Hand it ONLY: the lane's working directory, the lane spec path (docs/superpowers/specs/lane-<slug>.md), the diff rangegit diff origin/development...feat/<slug>, the repo's unit-test commands, and this brief:- Read the acceptance criteria and the diff. Write the unit tests the implementation is missing — one per real behavior or edge case (happy path, negative/error path, boundary), in the repo's existing test style and location. Test the contract in the spec, not the shape of the implementation.
- Do NOT modify non-test files. Do NOT weaken, skip, or delete an existing test. No coverage padding, no asserting constants or framework internals.
- Run the suite. Commit test files only.
- Final line:
UNIT-TESTS: PASS — <n> tests added(all green) orUNIT-TESTS: FAIL — <failing test> :: <the defect it exposes>.
FAIL→ a real defect: fix the source on the feature branch (root cause — systematic-debugging; never edit the agent's test to make it pass unless the test itself is provably wrong, and say so in the evidence), then re-run this stage.PASS→ccam stage tests --evidence "<n> independent unit tests added, green"→ Stage 2.
2 — Pre-push CI gates + dev preflight (on the feature branch)
ccam stage gates --status running— declare it FIRST. This is also the fix-loop's re-entry point, and the declaration is what moves the lane BACK down the pipeline: detection alone can't (recordDetectionis forward-only and never overrides a higher declared stage), so a re-entry that skips this line leaves the dashboard showing the stage you already left.ccam lanes hook ci-gate— runs the profile's CI gate (lint / test / contract checks) against an isolated per-lane test DB. On failure: read the output, fix on the feature branch, commit, re-run. Loop until green.ccam lanes sync-base --check feat/<slug>— the dev preflight: fetches and checks the branch against the CURRENTorigin/developmentwithout merging anything. Exit 5 on a migration-number collision: declareccam stage migration-collision --status running(an alias ofgates— the map stays put, the lane's stage names WHY it is sitting there), rename the printed file to the suggested number on the feature branch (git mv, update any in-file references), then re-run Stage 2. Exit 0 withDEV_DELTA:/DEV_OVERLAP:output otherwise — informational, you do NOT sync the branch for it (GitHub merges non-conflicting histories fine); a large overlapping delta is a heads-up that post-merge behavior may differ from what you test locally.
3 — E2E on the feature branch
The e2e hook doesn't run migrations itself — it tests the already-running stack. To exercise the feature's code and any new schema, boot the lane stack with the feature branch first:
ccam stage booting --status running— an alias ofe2e-feature; the boot below can take minutes and this says which minutes they are.ccam lanes up --qc— boots with the profile's QC env (mock/stub flags so QC is deterministic, fromQC_BOOT_ENV), applies the feature branch's own migrations, and reboots the stack. Idempotent; safe to re-run. (The branch was cut fromorigin/development, so this stack IS development + your feature as of the branch point.)ccam stage e2e-feature --status running, then launch a fresh-context agent (Agent tool,subagent_type: general-purpose, FOREGROUND — neverfork) to RUN and triage the suite. You booted the stack; you do not run or read the suite. Hand it ONLY: the lane's working directory, the feature branch, the acceptance points (lane spec path), and the e2e discipline verbatim —ccam lanes hook e2ewithrun_in_background: true, a backgroundedsleep 90beside it,ccam lanes logs <id> e2efor the tail on each wake, kill-and-FAIL past ~22 min, and NEVER a bare test-runner invocation in the lane (bare runs skip the cross-lane lock, the hard timeout and the heartbeat). Playwright e2e under the e2e lock against the now-booted stack. This is the only e2e gate in the flow — there is no dev-merged suite behind it.- It reports one final line:
E2E: PASSorE2E: FAIL — <spec/test> :: <root cause, and whether it looks like a product defect or an infra flake>.
- It reports one final line:
- On
FAIL: fix on the feature branch, commit, re-run from Stage 1b (source changed) or Stage 2 (test-only change). - Iterating on a failing spec: declare
ccam stage e2e-scoped(alias ofe2e-feature) so the dashboard shows this is a narrowed run, not the gate, and tell the e2e agent to run scoped through the hook —ccam lanes hook e2e -- <spec file/filter>(same agent, same discipline; the narrowing is in ITS brief, not in you taking the run back). A scoped green is never the gate; finish with the full suite (unless the localized fast-path applies — see Hard rules). - On success:
ccam stage e2e-feature-passed --status running
4 — Code review (no open PR yet — use local diff)
ccam stage review --status running, then launch a fresh-context reviewer agent (Agent tool,subagent_type: Code Reviewer— FOREGROUND, neverfork; a reviewer who already watched you write the code is not a reviewer). Hand it ONLY: the lane's working directory, the lane spec path (requirement + acceptance criteria), the diff commands, and the brief below. Do NOT summarize your own implementation for it, and do NOT pre-empt findings — let it read the diff cold.- Run the
code-reviewskill at efforthighon the feature diff vsorigin/development— the deterministic gate, not an ad-hoc read. No PR is open yet, so point it at the local diff:git diff origin/development...feat/<slug>(andgit log origin/development..feat/<slug>for commits). ONLY if thecode-reviewskill is unavailable in its session, fall back to a manual review of that diff (correctness, security, tests, migration/contract safety, this repo'sCLAUDE.md+.claude/rules/boundaries). - Final line:
REVIEW: CLEANorREVIEW: FINDINGS — <n>, with the findings listed above it aspath:line — severity — problem — suggested fix.
- Run the
- Triage its findings with receiving-code-review judgment (verify each, neither blind agreement nor blind rejection) and apply what's worth applying on the feature branch. Changed source → re-run from Stage 1b; changed only tests → from Stage 2. Keep the findings + your resolutions — the senior gate reads them.
- The Stage-6
qc-localreport covers the user-flow review for the senior gate. ccam stage review --evidence "<findings count> findings, resolved"
5 — QC plan (bound the test scope before any browser QC)
- Author a QC Plan the browser-QC agents (Stage 6 now, and a future Stage 13 once dev-QC exists) will execute against — so QC covers everything that matters and nothing that doesn't (no missed scenarios, no wandering into unrelated areas). Derive it from the acceptance points (lane spec) + the real change surface (
git diff origin/development...feat/<slug>and--stat). Three parts:- In-scope scenarios (numbered): each acceptance point with positive AND negative cases; adjacent flows sharing routes/components/data with the change; required state coverage (reload on each stateful screen touched, one logout→re-login, back/forth nav); and the required UI/UX layout checks for every form/screen the feature touches (narrow AND short viewport, expandables open so content exceeds the viewport, fixed chrome not clipped, every control labelled, section headers more prominent than field labels).
- Out-of-scope (explicit): areas NOT to test because the change cannot affect them — this is what stops QC from over-testing.
- Smoke set: login + main nav + ≥3 unaffected major areas.
- Append it to the lane spec under a
## QC Planheading (docs/superpowers/specs/lane-<slug>.md) — the same file the senior gate reads. You are the single writer of this section; the QC agent only proposes additions in its report and you fold them in (Stage 6). This keeps the plan race-free yet living. ccam stage qc-plan --status running
6 — Browser QC via the qc-local agent
- Test-only fast-path: on a fix-loop re-entry whose change is ENTIRELY test files (see the fix-loop rule), SKIP this stage — the app's runtime UI is unchanged — and record
--evidence "QC skipped: test-only change". Otherwise run it: ccam stage qc --status running, then launch the qc-local agent (Agent tool,subagent_type: qc-local, orccam:qc-localif the lane copy isn't installed — FOREGROUND, fresh context, neverfork; it gates the pipeline; the Setup section's agents preflight covers both names). Give it: the lane's working directory, the feature slug, the feature title, the acceptance points (lane spec), and the QC Plan (lane spec, Stage 5) as the authoritative scope to execute against — and NOTHING about how you built it. No "the button is at the top-right", no "this part already works", no known-issue list: a QC pass that starts from your mental model only re-checks what you already believed. It owns the whole local browser QC and proof capture (ccam lanes proof-linkfirst, then screenshots land under the proof gallery automatically). It runs against the lane's feature-branch stack from Stage 3. Do NOT drive the browser yourself at this stage.- Parse its last line:
LOCAL-QC: PASS→ continue.LOCAL-QC: FAIL — <reasons>→ fix on the feature branch → re-run from Stage 2. Keep its report — it is the feature user-flow review for the senior gate. - Fold back discoveries: if its report lists scenarios it found that weren't in the plan (its "Scenarios discovered during QC" section), add them to the
## QC Planin-scope list in the lane spec.
7 — Senior GO/NO-GO gate (authorizes the publish)
- Launch the senior-gate-reviewer agent (Agent tool,
subagent_type: senior-gate-reviewer, orccam:senior-gate-reviewerif the lane copy isn't installed — FOREGROUND, fresh context, neverfork; installed the same way asqc-local, see Stage 6). Give it: the lane's working directory, the requirement + Stage-0 answers (the lane spec file), the feature branch, the Stage-4 code-review findings + resolutions as the other agents wrote them (verbatim/by path — never your paraphrase of why each was fine), the Stage-6qc-localreport (the user-flow review), and the raw results of gates/e2e/QC. The agent inspects the local diff withgit diff origin/development...feat/<slug>— no open PR is required (and none exists yet). - Parse its final line:
VERDICT: GO→ proceed to Stage 8.VERDICT: NO-GO — <fixes>→ccam stage gate-blocked --evidence "NO-GO — <reason>"(an alias ofgate, so the map holds while the lane's stage name says the gate refused), then fix on the feature branch and re-run from Stage 2. No attempt cap — the loop re-enters; the dashboard's time-on-stage surfaces a lane stuck cycling so the human can step in. Set--status blockedonly for a genuine blocker you can't resolve.
ccam stage gate --evidence "GO"(orNO-GO — <reason>)
8 — Publish: push branch + open/update PR (GATED — only on GO)
ccam stage publishing --status runningccam stage push-revalidate(alias ofpr-open) — then re-run the preflight:ccam lanes sync-base --check feat/<slug>— development may have moved while you were in QC. A migration collision here (exit 5) sends you back to Stage 2 with the rename; a clean result (exit 0) proceeds to the push.git push -u origin feat/<slug>— first push of the feature branch to remote. All gates have passed before this point; the PR is finalized before reviewers see it.- Open or update the PR based on and targeting
development:gh pr create --base development --fill(orgh pr edit/ the push itself if a prior run already created it). Capture the URL. ccam stage pr-open --evidence "<pr-url>"— the dashboard shows the PR link from here (via--evidenceinccam feature show/ccam lanes).
9 — Ticket (currently SKIPPED — tracker integration is off)
- Tracker integration is hardcoded off (see Setup). Do nothing here; do not attempt to file a ticket. When F ships
ccam lanes integration tracker, this stage gets a real implementation. (Checkccam lanes integration trackerin the heartbeat: even when on, no ticketer agent exists yet to act on it.)
10 — CI watch on the PR (non-blocking)
- Check the PR's CI with
gh pr checks/gh. Green → continue to the report + watch — never idle waiting for green. - Red CI → triage before the fix-loop (shared CI flakes under multi-lane load; treating every red as your defect wastes cycles):
- Read WHICH job/tests failed with
gh(there is noccam ciyet — F builds that; usegh run view/gh pr checks --watchdirectly). - Your tests / your code implicated → real failure: fix on the feature branch, re-enter from Stage 2.
- Infra/flake signature (a job with no test failures, OOM/contention on the shared runner, a hung job with no output, or a test that is green locally on the identical tree) → re-run the workflow via
gh run rerun <run-id> --failed— ONCE. Still red after the rerun → treat it as real (or escalate with the evidence). Never rerun more than twice, and never push an empty commit to re-trigger CI. - A hung workflow (running way past its normal duration with no output) →
gh run cancel <run-id>then rerun once. - Still red after your fix, on the same job: you have now mis-triaged it once — hand the triage to a fresh-context agent (
general-purpose, FOREGROUND, neverfork) with the run URL, the failing job's logs and the diff range, and NOT your theory of the cause. Calling your own failure a flake twice is how a real defect gets pushed past the gate.
- Read WHICH job/tests failed with
11 — Report
- Post a concise report: PR URL, CI status, what shipped, and that the PR now awaits a human merge (this pipeline never merges).
ccam stage reported --status running- Do NOT clear or reset the lane's feature state. Cleanup is the human's call, from the dashboard — they may still be manually testing.
12 — Watch the PR (until a human merges or closes it)
ccam stage watching-pr --note "watching PR for comments + base conflicts + the merge"- Loop every ~5 minutes, paced by a backgrounded wait so the turn isn't pinned (see the waiting-primitive rule above): run
sleep 300withrun_in_background: true— you're re-invoked when it elapses. Each iteration:- Check PR state:
gh pr view <pr_url> --json state,mergeable -q '.state + " " + (.mergeable|tostring)', and bump the heartbeat (ccam stage watching-pr). MERGED→ a human merged it: go to Stage 13 (post-merge verification).CLOSED(unmerged) → the human rejected/abandoned it:ccam stage done --status passed --note "PR closed unmerged by human"→ STOP.CONFLICTING→ the feature branch conflicts withdevelopment. Base guard first: check the PR's actual base —gh pr view <pr_url> --json baseRefName -q .baseRefName. If it is NOTdevelopment(the base drifted — a human retargeted the PR, or it predates this pipeline), do NOT auto-merge anything:--status blocked --note "PR base is not development — human decision"and STOP. If the base ISdevelopment, declareccam stage sync-conflict --status running(an alias ofgates, which is also where the re-entry below lands — one declaration, honest about both) and resolve it as real work:ccam lanes sync-base feat/<slug>(merges the latestorigin/developmentINTO the feature branch — the only sanctioned merge). A migration-number collision (exit 5) means nothing was merged — rename the printed file on the feature branch, re-run Stage 2, then retry this step.- Exit 4 — merge conflict, left in place on purpose. Resolve every conflict thoughtfully on the feature branch — keep
development's behavior for code unrelated to this feature, preserve the feature's intent where they overlap; when genuinely ambiguous, STOP and escalate (--status blocked, note the files) rather than guess. Never hand-merge a generated contract/client file listed in the profile'sGENERATED_MERGE_PATHS— the keep-ours driver + regen own them.git addONLY the conflicted files,git commit --no-edit, thenccam lanes sync-base --continue feat/<slug>(folds any regenerated artifacts into a follow-up commit). - Re-enter the pipeline from Stage 2 through Stage 8 (the push updates the PR), then return here and keep watching.
- For each new PR comment (list with
gh pr view <pr_url> --json comments, tracking which you've already handled by comment id in your own notes), triage AND always reply on its thread (every comment gets a response — no silent handling, so reviewers see it was considered):- Worth fixing (reviewer-requested change, real bug, test/doc gap): this is a NEW CHANGE —
ccam stage pr-comment-fix --status running(alias ofwatching-pr, so the lane reads as acting on a comment rather than idly polling), then apply it on the feature branch and re-enter the pipeline from Stage 2 through Stage 8 (+ Stage 10 CI watch). The full process applies; no shortcuts because "it's just review feedback". After the fix is pushed, reply to the comment confirming resolution — what changed + the commit/PR ref — by writing the reply to a file and postinggh pr comment <pr_url> --body-file <path>referencing the comment. Never inline--body "..."— bodies carry backticks/file:line/$(...)that bash reads as command substitution inside double quotes, which corrupts the comment and trips an approval prompt. Then come back here and keep watching. - Question / discussion: answer it via
gh pr comment <pr_url> --body-file <path>(same file-not-inline rule) — no code change. - Not worth fixing (out of scope, working as intended, deferred): reply with the reasoning so the reviewer knows why it wasn't actioned (don't just skip it).
- Sign every reply with a distinct attribution — end each posted body, on its own line, with:
— 🤖 ship-feature-lane pipeline.
- Worth fixing (reviewer-requested change, real bug, test/doc gap): this is a NEW CHANGE —
- Nothing new → background another
sleep 300(run_in_background: true) and end the turn; you'll be re-invoked for the next poll. A PR can sit for days — that's fine.
- Check PR state:
13 — Post-merge verification (currently SKIPPED — CI-wait and dev-QC integrations are off)
ccam stage merged --status running --note "PR merged — post-merge verification unavailable (F not built)"- Both dev-CI-wait and dev-QC are hardcoded off (see Setup). Go straight to Stage 14. When F ships these integrations, this stage gets its real implementation (mirroring Shipyard's: dev CI watch, then a
dev-qcagent QCing the deployed site, looping to a Stage 15 follow-up-fix pattern on any issue found).
14 — Done
- Post the final report: PR merged, that post-merge verification is unavailable pending F.
ccam stage done --status passed --note "merged; post-merge verification unavailable (F not built)"→ STOP (leave the lane for the human to clear from the dashboard whenever).
Escalation
Whenever you STOP early (an ambiguous merge conflict, an unexpected/unrecoverable failure, a missing agent this skill depends on), set ccam stage <current> --status blocked --note "<what the human must decide>" — the dashboard surfaces it. Then summarize for the human and wait.