# E3 — Agents port: qc-local + senior-gate-reviewer

**Status:** approved 2026-08-05. Roadmap it belongs to:
`docs/superpowers/plans/2026-08-03-shipyard-parity-lanes.md` (subsystem **E**).

## Problem

`.claude/skills/ship-feature-lane/SKILL.md`'s Stage 6 (browser QC) and Stage 7 (senior GO/NO-GO gate) invoke the `qc-local` and `senior-gate-reviewer` subagents by `subagent_type`, but neither exists in this repo — a lane reaching either stage is currently marked `blocked`. Shipyard's source (`~/MyDrive/Projects/ResearchAndDevelopment/AgentWorkflow/claude/agents/{qc-local,senior-gate-reviewer}.md`) is the reference. The roadmap names five agents for E ("Agents"); this spec ports the two the current skill text actually invokes.

## Scope

**In scope:**
1. `server/data/agent-templates/ship-feature-lane/qc-local.md` — the ported browser-QC agent template.
2. `server/data/agent-templates/ship-feature-lane/senior-gate-reviewer.md` — the ported publish-gate agent template.
3. `ccam lanes agents install [<id>]` — copies both templates into `<lane>/.claude/agents/`, git-excluded.
4. `docs/LANES.md` + roadmap progress line.

**Out of scope (later, when their dependencies exist):**
- **`ticketer.md`, `dev-qc.md`** — both are invoked only by Stages 9 and 13, which are hardcoded-skipped pending F's tracker/dev-QC integrations. Porting them now would ship agent files nothing calls.
- **`pr-reviewer.md`** — not referenced anywhere in this repo's `SKILL.md` (the CCAM port dropped the reference during E1; confirmed by grep). Nothing to unblock.
- **Per-lane embedded credentials** — Shipyard's installer injects a real QA account's email/password directly into `qc-local.md`'s prompt text, sourced from a per-lane seeded account (`.harness-qa.env`) that has no CCAM equivalent (no seed-account system, no matching `secrets.js` category). This spec ports the agent's *fallback* path (no embedded creds → report `FAIL` with a clear reason) rather than inventing a seed-account system to unblock a credentials feature nobody asked for yet.
- **MCP auto-provisioning** (`ccam lanes mcp sync`) — F, not built. `qc-local.md` keeps referencing the `playwright-qa-local` MCP server name by convention; `SKILL.md`'s Stage 0 already documents the manual-`.mcp.json` fallback this depends on.
- **`review-checks.md` reader command** — `senior-gate-reviewer.md` reads the profile's optional `review-checks.md` directly with the Read tool (a plain file, no CLI needed), not through a new `ccam lanes profile cat` command.

## Decisions

| | Choice | Rationale |
|---|---|---|
| Template location | `server/data/agent-templates/ship-feature-lane/*.md` | Parallel to `server/data/pipelines/*.json` (E1's pattern) — data files, not source. **Not** `ccam-lanes/.claude/agents/`, which already holds this repo's own dev-facing agents (`backend-reviewer`, `frontend-reviewer`, `mcp-reviewer`); mixing a lane's runtime-installed agents into that directory would be a name-collision risk and conflate two different agent populations. |
| Heartbeat calls (`state.sh set` between QC scenarios) | Dropped entirely | Shipyard dispatches these agents as a separate background-capable process, so a stalled multi-minute run needs an explicit touch to avoid a false "stalled" flag. CCAM's `qc-local`/`senior-gate-reviewer` run as a synchronous Agent-tool call inside the driving session's own turn — the turn is blocked on the subagent's return, so the session is already "alive" by definition for the whole run. No CCAM heartbeat primitive exists for a subagent to call anyway (this repo's own rule: only `ccam stage` writes progress, and these agents must never call it). |
| Auto pre-seed login (`lane-qa-login.sh`, `lane-reset.sh`) | Dropped; `qc-local.md`'s login fallback becomes an honest failure, not a script call | CCAM has no seeded-QA-account system these scripts assumed. Calling a command that doesn't exist would be the same "docs must be executable" violation E1's Decisions table already ruled against for hardcoded-off integrations — this spec's version reports `LOCAL-QC: FAIL — login required, no seed-account mechanism configured` instead. |
| `lane-sync-dev.sh --check` reference (senior-gate-reviewer's merge-safety check) | → `ccam lanes sync-base --check <branch>` | Direct 1:1 mapping — E2 already built and shipped this command with the exact contract (exit 5 collision, `DEV_DELTA`/`DEV_OVERLAP` output) the agent's text already describes. |
| `@@HARNESS_ROOT@@` / `.harness-lane` | Dropped | Same reasoning as E1's skill port — CCAM resolves a lane from `cwd` directly. |
| Install command semantics | `ccam lanes agents install [<id>]`, idempotent (overwrite-on-reinstall, not merge) | Matches the "explicit, never automatic" shape this repo already uses for `proof-link` and the E2 merge-driver setup — a session calls it when it wants updated templates, the dashboard never calls it for them. |
| `.claude/agents/` git-exclusion | Appended to `<lane>/.git/info/exclude` (clone-local, not the tracked `.gitignore`) | Same mechanism `write_lane_markers` used in Shipyard and the same file this repo's proof-link machinery already touches — a lane-runtime concern never becomes a change to the app repo's own tracked ignore rules. |

## Command mapping

| Shipyard | CCAM | Status |
|---|---|---|
| `lane-agents-install.sh <N>` | `ccam lanes agents install [<id>]` | **new in this task** |
| `state.sh "$N" set` (mid-run heartbeat) | dropped — see Decisions | n/a |
| `lane-qa-login.sh "$N" local` | dropped — see Decisions | n/a |
| `lane-reset.sh "$N"` (reseed on login failure) | dropped — see Decisions | n/a |
| `lane-sync-dev.sh "$N" --check <branch>` (senior-gate-reviewer's merge-safety check) | `ccam lanes sync-base --check <branch>` | exists (E2) |
| `lane-up.sh "$N" --no-build` (revive a down stack) | `ccam lanes up --no-build` | exists (E1 runtime, pre-existing flag) |
| `profile-cat.sh review-checks.md` | Read tool on `<lane>/.ccam/profile/review-checks.md` directly | no command needed |
| `"$HARNESS/bin/lane-mcp-sync.sh"` (implied MCP setup) | manual `.mcp.json` — `SKILL.md` Stage 0 already documents this fallback | F (deferred) |

## Design

### Agent templates

Both files are near-verbatim ports of the Shipyard source with the Command-mapping substitutions applied and the out-of-scope credential/heartbeat/login sections rewritten per the Decisions table:

- **`qc-local.md`** — frontmatter (`name`, `description`, `model: opus`, `color: green`) unchanged in shape. Body: drops the heartbeat loop and the `@@HARNESS_ROOT@@`/`.harness-lane` setup; the "Account / login" section keeps the "if a credentials block is embedded at the top of this agent, use it" path (future-proofed for when a seed-account system exists) but replaces the auto-login-script fallback with an honest `FAIL`. Proof convention (`proof/<feature-slug>/qc-local/<NN>-<what>.png`) is unchanged — it already matches this repo's proof gallery (C) verbatim.
- **`senior-gate-reviewer.md`** — frontmatter unchanged (`model: opus`, `color: red`). Body: the merge-safety check's command becomes `ccam lanes sync-base --check <branch>`; the stack-specific-checks step reads `review-checks.md` with the Read tool if present, otherwise falls back to "derive the equivalents yourself" exactly as the original already instructs when that file is empty. Nothing else changes — this agent never touched credentials or heartbeats to begin with.

### `ccam lanes agents install`

- `POST /api/lanes/:id/agents/install` — reads both templates from `server/data/agent-templates/ship-feature-lane/`, writes them to `<lane>/.claude/agents/{qc-local.md,senior-gate-reviewer.md}` (creating the directory if needed), appends `.claude/agents/` to `<lane>/.git/info/exclude` if not already present (same idempotent read-existing-then-append pattern E2's `setupMergeDriver` already established for `.git/info/attributes`). Returns `{installed: ["qc-local.md", "senior-gate-reviewer.md"]}`.
- No templating/placeholder substitution needed at install time — both files are already CCAM-final text (all substitutions happened once, at port time, into the template files themselves). This is a deliberate simplification from Shipyard's Python templating step, which existed only to inject per-lane credentials and MCP names this scope doesn't have.
- CLI: `ccam lanes agents install [<id>]`, same `resolveLaneArg` pattern as `lanes proof-link`.

## Invariants

1. **CCAM still does not orchestrate.** `agents install` copies static files and returns; nothing about it triggers, gates, or sequences pipeline stages.
2. **Neither agent ever writes `stage`/`status`/`notes`.** Both already say so explicitly in their own prompt text (ported verbatim); this spec doesn't change that boundary.
3. **A lane that never runs `agents install` sees no behavior change** — Stage 6/7 remain `blocked` exactly as today until a session explicitly installs the agents, same "explicit, never automatic" shape as `proof-link`.

## Verify

Unit tests for the install route/CLI (template files land at the right path, git-exclude line is appended idempotently on a second call, a lane with no `.git` directory yet fails cleanly) — no new git-internals risk here (this is plain file I/O, not the git-worktree subtlety E2 had to test for). A manual read-through of both ported agent files against their Shipyard originals, confirming every `@@HARNESS_ROOT@@`/`.harness-lane`/removed-script reference is actually gone (not just the ones this spec called out) is part of the implementation task, not a unit test — prose files don't have an automated "no dead references" check beyond that read.
