implement · git:20260901.4512c10 · 2026-09-01 · sha256 63bb755b048565eb

implement git:20260901.4512c10A

Immutable. This exact content is served forever at /api/v1/blob/63bb755b048565eb.

---
name: implement
description: Implement an approved issue — run tdd-developer → review panel → fix loop → build-engineer → docs polish in the issue's own worktree, then push the branch and open a PR. Defaults to an agent team led by issue-manager (SPEC_FLOW_IMPLEMENT_MODE=team, requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1); falls back automatically, or via SPEC_FLOW_IMPLEMENT_MODE=workflow, to the original Workflow-tool script. A type:docs issue instead runs a single lightweight doc-writing pass with architect available on demand, skipping the review panel/build/polish entirely. A type:tech-debt issue runs the full review panel as normal (behavior-preservation mode, no spec) but works from the issue's own Direction instead of tasks.md, and opens its own draft PR after the first commit since none exists yet. Third stage of the flow delivery workflow (see docs/workflow.md). Requires the owner to have approved the plan first — a committed spec, or for a content-only type:docs/type:tech-debt issue, its scope + acceptance criteria (or Direction). Invoking this skill is the explicit opt-in to that orchestration, whichever mode.
argument-hint: [issue number, with its plan already approved]
---

# implement — build the approved spec, open a PR

You are this issue's `issue-manager`, running as your own dedicated background session. The owner has
**approved the plan** for issue `#N` — a committed spec, or for a content-only `type:docs` issue,
its scope + acceptance criteria, or for a `type:tech-debt` issue, its confirmed Direction (see Input
below). Drive the implementation team to completion and
open a review-ready PR — by default as an **agent team** you lead (see step 4), which is exactly
what running as your own top-level session (not a subagent) makes possible at all: a team needs a
lead, only a top-level session can be one, and a subagent can never spawn its own team. Where
agent teams aren't available or wanted, the same work runs instead as the original `Workflow`-tool
script — same panel, same rules, no team. **Invoking this skill is the owner's explicit
opt-in** to that orchestration, whichever mode it resolves to.

Input: an issue number `#N`, normally with an OpenSpec change `issue-<N>` — deterministic, from
`activate`. You're already running inside this issue's worktree — Claude Code's own
background-session isolation put you there, on whatever branch it assigned; resolve it with `git
rev-parse --abbrev-ref HEAD` rather than assuming a name. If `openspec/changes/issue-<N>` isn't
there: for a `type:docs` issue this is expected (a content-only docs change generates no spec —
see step 4's docs fast path); for a `type:tech-debt` issue this is **always** expected — the fast
path never generates one (see step 4's tech-debt handling); otherwise, list `openspec/changes/`
(excluding `archive/`) and orient yourself in whatever is — it may predate this naming.

## Steps

1. **Check the repo's configuration first, before any work.** Run:
   ```bash
   bash ${CLAUDE_PLUGIN_ROOT}/scripts/repo-config.sh check
   ```
   Non-zero: **stop here**, having done nothing, and relay the script's output **verbatim**. Add no
   rules, no explanation, and no fallback of your own — the script's message is complete, and this
   plugin ships no default policy to fall back to. Do not offer to create the file; that offer is
   `project-manager`'s alone.

   **Confirm the precondition.** The issue must be `status:spec-review` AND the owner must have
   approved — either in the conversation, or, if `.spec-flow/owner-instructions` (read fresh here)
   explicitly auto-approved the spec/plan for this run, that counts too (the normal case when
   `activate` launched you directly per its own auto-approve path). If you can't confirm either,
   ask before proceeding. Flip the label to in-progress:
   ```bash
   gh issue edit <N> --remove-label status:spec-review --add-label status:in-progress
   ```

2. **Open a draft PR early — keep CI warm.**

   **2a. Resolve the values first — this half NEVER skips.** Steps 4 and 5 consume `<DEFAULT_BR>`
   and `<PR>`, and this is the only place they are resolved, so skipping the whole step leaves them
   undefined on exactly the paths that skip it:
   ```bash
   BR=$(git rev-parse --abbrev-ref HEAD)
   DEFAULT_BR=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
   PR=$(gh pr list --head "$BR" --json number --jq '.[0].number // empty')
   echo "DEFAULT_BR=$DEFAULT_BR"
   echo "PR=$PR"          # empty is expected on a fast path — 2b explains who opens it and when
   ```
   Resolve `$DEFAULT_BR` from the repo, never assume `main`: it must match what `EnterWorktree`
   branched this worktree from. An empty `PR` here is not an error on a fast path; it means the PR
   does not exist yet, and you re-resolve it with this same command once it does (see 2b).

   **2b. Open the PR. Skip 2b — and only 2b — if `openspec/changes/issue-<N>`
   doesn't exist AND the issue carries `type:docs` or `type:tech-debt`** — neither fast path commits
   a spec, so the branch has no commits ahead of the default branch yet and `gh pr create` would
   fail outright (GitHub rejects a PR with no diff). For `type:docs`, step 4c opens the PR itself,
   using this exact mechanics, right after its own first commit lands. For `type:tech-debt`, step
   4a's Implement teammate/agent makes the first commit — **you** (the lead) open the PR yourself,
   same mechanics, right after it reports back and before moving to step 4b's review panel, in Team
   mode; in Workflow mode the script's Implement-phase agent opens it itself (the one narrow
   exception to its own GUARDRAILS — see `implement.workflow.js`), since nothing outside the script
   regains control mid-run to do it the way the Team-mode lead can. **In that Workflow-mode case
   the script does not return the PR number, so re-run 2a's `gh pr list --head "$BR"` command once
   the script returns, and use the number it prints as `<PR>` for step 5.** Without it step 5 has
   no PR to mark ready, write a body on, or merge, and the run ends with the PR stuck as a draft.
   (A missing `openspec/changes/issue-<N>` on an issue carrying **neither** label means something
   else — most likely a legacy change predating this naming; see the Input note above — not
   "nothing to push yet," so don't skip this step for that case.) **Otherwise** push the branch (it already
   carries the committed spec) and open a **draft** PR *now*, before
   implementation runs. CI triggers on `pull_request` and runs on draft PRs, so from here every
   checkpoint push during implementation exercises whatever this repo's CI does, in parallel with
   local work, instead of leaving it idle until the end. (In a repo whose policy says CI is not a
   test gate, this costs nothing and still gets the PR open early, which is the other half of why
   it is here.) **Re-running this skill is normal** (resuming after a crash, after residual findings, or
   after the owner sends you back) — check for an existing PR first and reuse it rather than
   erroring on a duplicate:
   ```bash
   BR=$(git rev-parse --abbrev-ref HEAD)
   DEFAULT_BR=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
   git -C <worktree> push -u origin "$BR"
   PR=$(gh pr list --head "$BR" --json number --jq '.[0].number // empty')
   if [ -z "$PR" ]; then
     gh pr create --draft --head "$BR" --base "$DEFAULT_BR" \
       --title "<issue title>" \
       --body "Closes #<N>

   Draft — implementation in progress. Tests run per this repo's own policy; see spec-flow/TESTING.md."
     PR=$(gh pr list --head "$BR" --json number --jq '.[0].number // empty')
     gh issue comment <N> --body "🚀 Draft PR #$PR opened — implementation starting."
   fi
   echo "DEFAULT_BR=$DEFAULT_BR"
   echo "PR=$PR"
   ```
   Steps 4/5 use `<DEFAULT_BR>` and `<PR>` as the literal values printed here, never as shell
   variables — variables don't survive separate Bash calls, and the `Workflow` tool's JSON `args`
   isn't shell-interpolated. Resolve `$DEFAULT_BR` from the repo, never assume `main`: it must
   match what `EnterWorktree` branched this worktree from.

3. **Generate the TEST INSTRUCTION — a pointer, never a policy.** This plugin holds no test or CI
   policy of its own. The repo states its own, and the teammates read it. Generate the line once,
   here, and never write a second copy of it anywhere:
   ```bash
   bash ${CLAUDE_PLUGIN_ROOT}/scripts/repo-config.sh instruction
   ```
   That stdout — one line — is the **TEST INSTRUCTION**. Append it **verbatim** to the prompt of
   every teammate you spawn below that runs tests: the implementer, every fix round, the
   review lenses, and step 5's `fix-ci`. (`build-engineer` gets the same line too, for its format
   and lint gate rather than for tests — step 4e says why. That is the only teammate outside this
   list, so do not read this list as forbidding it.) Do not paraphrase it, summarize it, or wrap it
   in a tier name, a test command, a stack probe, or a fallback of your own. Step 1's check has
   already guaranteed the policy file is there, so the line carries no missing-file clause and you
   must not add one.

   You do not need to read the policy yourself. Whatever it says — the fast tier locally, the whole
   suite locally, or nothing at all because the repo has no test suite — is what the team follows,
   and none of it is this skill's to restate. See **Test policy** in `docs/workflow.md`.

4. **Resolve the implement mode, then drive Implement → Review → Fix (bounded) → Build → Polish.**

   **Resolve the developer agent first.** Read `SPEC_FLOW_DEVELOPER_AGENT` from the environment.
   If it is unset or empty, the developer agent is `tdd-developer`, the bundled default; this is
   the behavior that shipped before the setting existed. If it is set, the developer agent is the
   agent it names, for example `rust-dev` from the standalone `dev-skills` plugin (see **Developer
   agent** in `docs/workflow.md`). If the named agent cannot be resolved, stop and report the
   missing agent by name; never fall back silently, because a configured agent that vanishes takes
   its discipline with it. **Everywhere below that says `tdd-developer` means this resolved
   developer agent**, in every mode and on every path, including the docs fast path, the fix loop,
   the polish pass, and the CI fix respawn. Only the review-panel agents and `build-engineer` are
   always the bundled ones.


   **Docs fast path.** If the issue carries `type:docs` (set at `groom`, carried through
   `activate` — see **Docs fast path** in `docs/workflow.md`), skip everything else in this step —
   Team/Workflow mode, the review panel, the fix loop, Build, Polish — entirely and run this
   instead. **Mode-independent**: it's one plain subagent spawn (the Agent tool, `tdd-developer`
   agent type, same mechanism `activate` step 3 uses for `architect`), not an agent-team teammate,
   so `SPEC_FLOW_IMPLEMENT_MODE`/`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` don't apply here.
   a. Spawn ONE `tdd-developer` subagent, in `<worktree>`. **Check for a spec first** —
      `ls openspec/changes/issue-<N> 2>/dev/null`: **present** (a structural/tech-accompanying
      `type:docs` issue, per `activate` step 5) → work `tasks.md`, updating exactly the
      documentation the spec describes. **Absent** (the common case — `activate` skipped spec
      generation for a content-only docs change) → work directly from the issue's own scope and
      acceptance criteria instead (`gh issue view <N> --json title,body`) — there is no `tasks.md`
      for this issue, and there won't be one; that's expected, not a sign something's missing.
      Either way: updating exactly the documentation described (README, a docs/mdBook tree,
      comments — no behavior change). Append the implementer GUARDRAILS (below). **If it hits a
      real question about whether the documentation matches the intended architecture/design**
      (not just wording), have it stop and report the specific question back to you rather than
      guessing — it should not try to reach `architect` itself.
   b. **If it reports back with an architecture question**, spawn `architect` yourself — read-only,
      same as `activate` step 3 — with that specific question, then spawn a **fresh**
      `tdd-developer` subagent with the answer plus everything from step a's prompt to finish the
      work (spawn fresh — plain subagents can't be resumed). Architect-on-demand, not a mandatory
      gate; most `type:docs` runs never trigger it.
   c. Once the docs pass reports done: **re-resolve `BR` fresh here** — `BR=$(git -C <worktree>
      rev-parse --abbrev-ref HEAD)` — variables from step 2's Bash call don't survive to this one,
      and step 2b may not have run at all on this path. **First confirm the branch is pushed** — the
      GUARDRAILS only say the teammate *may* push, not must, so `git -C <worktree> push -u origin
      "$BR"` yourself if it hasn't happened. **If step 2b was skipped** (no spec existed when this
      run started), open the draft PR now using step 2's *full* mechanics — including its
      existing-PR reuse check (`gh pr list --head "$BR" --json number`; only `gh pr create --draft`
      if none found), since this may be a re-run where 4c already opened it once — there's a real
      commit to open it against now. Then
      comment `gh issue comment <N> --body "📚 Docs updated."`, note a one-line summary of what was
      documented (step 5 uses it in place of `review_summary`), then go straight to step 5 — no
      review panel, no build step. A docs-only change has no code to lint/build/review through five
      lenses built for behavior.
   This is the ONLY thing that skips the review panel — every other issue, however small, still
   goes through it in full. **A `type:tech-debt` issue does NOT take this docs branch** — it still
   needs real code review, so it goes through the normal Implement → Review → Fix → Build → Polish
   sequence below like any other issue; the only difference is what `CHANGE_PARAM` resolves to.
   Otherwise, for every non-`type:docs` issue:

   **Resolve `CHANGE_PARAM` before anything else in this step** (used in step 4a's Implement prompt
   and step 4b's identical review-panel prompt below, both modes): `ls openspec/changes/issue-<N>
   2>/dev/null`. **Present** → `CHANGE_PARAM = "issue-<N>"`, the normal case. **Absent** — only ever
   valid for a `type:tech-debt` issue (see the Input note above; any other issue with no change
   directory is the legacy-naming case, not this) — `CHANGE_PARAM = "none — type:tech-debt fast
   path"`, the literal sentinel `agents/reviewer.md` and `implement.workflow.js` both key off of to
   switch into behavior-preservation mode.

   **Resolve `BREAKER_PARAM` here too, but only when `CHANGE_PARAM` is the tech-debt sentinel** —
   the refactor circuit breaker (see **Refactor circuit breaker** in `docs/workflow.md`). It applies
   to **behavior-preserving runs only**. Under ordinary feature TDD, editing one test file three
   times is routine — several tests for one module — so appending it on the normal path would stall
   almost every run. On the normal path, set `BREAKER_PARAM = "off"` and append nothing. The
   non-configurable triage gate in `agents/tdd-developer.md` still applies on every path; only this
   mechanical backstop is scoped.

   On the tech-debt path, read `SPEC_FLOW_REFACTOR_BREAKER` from the environment; unset or
   unrecognized → `ask`, the default. In **Team mode** each value maps to one BREAKER SENTENCE you
   append to the implementer GUARDRAILS below:
   - **`ask`** (default) → *"If you have edited the same test file more than twice in this run,
     STOP: leave the tree exactly as it is, do not revert, and report the blocker and the
     classification you could not make. Wait for my decision — do not keep editing that file."*
   - **`revert`** → *"If you have edited the same test file more than twice in this run, STOP:
     revert to the last green commit (`git reset --hard <sha>` on the issue branch only) and
     report the blocker and the classification you could not make. Do not keep editing that
     file."*
   - **`off`** → no BREAKER SENTENCE at all. Append nothing.

   In Team mode a stopped teammate messages you mid-run: surface it to the owner, let them choose
   continue or revert, then respawn a fresh `tdd-developer` with their decision. **In Workflow mode
   you append nothing** — you pass `BREAKER_PARAM` in the script's `args` and the script composes
   its own equivalent sentences, worded for a script that cannot pause: they ask the agent to
   prefix its summary with the token `BREAKER-STOP:`, and on seeing it the script returns
   immediately with `approved: false` and the stop in `residual_findings`, skipping the panel
   entirely. The owner sees the stop when the run returns, not during it. Never let a fix round
   continue past a trip — a fresh agent's "in this run" counter resets, so it would resume editing
   the file the breaker just stopped.

   `SPEC_FLOW_IMPLEMENT_MODE` — `team` (default) or `workflow`. `team` is an agent team led by
   you, spawned fresh each run — richer (teammates message each other, self-claim work) but
   experimental and token-heavier. `workflow` is the original bounded `Workflow`-tool script
   (`implement.workflow.js`) — the same panel and the same merge/approve/fix-loop rules,
   just scripted instead of reasoned through, for when agent teams aren't available or wanted. If
   the env var is unset or `team` and `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` is **not** set, fall
   back to `workflow` automatically and say so — don't fail the run over a missing opt-in flag.
   Then follow **either** "Team mode" **or** "Workflow mode" below, never both.

   **Team mode (default).** Every teammate is spawned from this plugin's own subagent definitions
   — reference them by name (`tdd-developer`, `reviewer`, `code-reviewer`, `security-reviewer`,
   `test-rigor-reviewer`, `observability-reviewer`, `build-engineer`) so each teammate gets that
   agent's tools/model, with your spawn prompt appended as additional instructions. **If a
   bare-name spawn fails "not found"**
   (no repo/user override registered under that name, and the plugin's own bundled agent isn't
   reachable by its bare name in this environment — Claude Code does not fall back to the plugin's
   namespaced form on its own), retry as `spec-flow:<name>` yourself; see the README's Override
   note and `implement.workflow.js`'s `agentNS()`, which applies this same fallback automatically
   in Workflow mode. Give every teammate a **GUARDRAILS** block —
   two variants, below — so none of them push to `main`, touch another issue, or take outward
   GitHub action; that's yours alone. `base` = `origin/<DEFAULT_BR>` — the literal branch name
   printed in step 2, not a shell variable (don't assume `main`); the review lenses diff
   `base...HEAD` in the worktree, so a wrong base reviews
   the wrong range. Track `tests_ran`, `tests_detail`, `spec_conformance`, `approve`,
   `review_rounds`, `residual_findings`, `non_blocking_findings`, `panel_ran`, and `review_summary`
   as you go —
   step 5's PR body needs them; there's no script returning them for you now.

   **GUARDRAILS (implementer teammates — tdd-developer, build-engineer):**
   > GUARDRAILS (strict): Operate ONLY inside the worktree, on the issue branch. You MAY `git
   > push` the issue branch to its own remote at checkpoints — usually so CI runs on an
   > already-open draft PR (push somewhat frequently — after a completed task or a few green
   > cycles — not on every commit); on your very first push there may be no PR yet (the lead opens
   > it right after), which is expected, not an error. Do NOT create or edit GitHub issues, do NOT
   > create/modify/mark-ready any PR yourself even if none exists yet — that's the lead's job, not
   > yours — do NOT post GitHub comments, do NOT push to `main` or any branch other than the issue
   > branch, and do NOT take any other outward or destructive action. If you discover follow-up
   > work, related bugs, or
   > candidate new issues, LIST them in your final report for the owner to triage — never file
   > them yourself. Backlog creation and prioritization are the owner's job, not yours.
   > **The approved spec is READ-ONLY** (omit this sentence for a `type:tech-debt` issue, which has
   > no spec): never create, edit, or delete anything under `openspec/changes/<change>/` to resolve
   > a finding — not the spec, not its scenarios, not `ac-coverage.md` or `overrides.md`. The owner
   > approved that spec at Seam 1 and the review panel diffs your code against it as committed, so
   > editing it to match the code silently launders their approval instead of fixing anything.
   > `tasks.md` checkboxes are the ONE exception — tick those as you go. If a finding can only be
   > resolved by changing the spec, the spec is what's wrong: STOP and say so, prefixed
   > `SPEC-DEFECT:`, naming the requirement and why the code cannot satisfy it as written.

   **On a tech-debt run in Team mode**, append the **BREAKER SENTENCE** resolved above to this
   block for the implementer spawns only — step 4a's Implement, every fix round in step 4d, and
   step 5's `fix-ci`. Never append it to `build-engineer` or the docs polish pass, and never on the
   normal (spec) path. If `BREAKER_PARAM` is `off`, append nothing anywhere.

   **REVIEW GUARDRAILS (review-lens teammates — everyone else in step b):**
   > GUARDRAILS (strict): You are reviewing, not implementing. Operate ONLY inside the worktree.
   > Running the repo's own format/lint/build/test commands to verify your findings is fine — the
   > `spec` lens needs that to honestly report `tests_ran`/`tests_detail`/`spec_conformance` (the
   > other four lenses leave those fields as their own agent file directs) — but you may not change the
   > tree: do NOT commit, do NOT `git push`, do NOT create or edit GitHub issues, do NOT create/modify/mark-ready any PR,
   > do NOT post GitHub comments, and do NOT take any other outward or destructive action — your
   > output is the JSON review contract, nothing else. If you discover follow-up work, related
   > bugs, or candidate new issues, LIST them in your
   > findings/summary for the owner to triage — never file them yourself.

   a. **Implement.** Spawn one teammate, `tdd-developer`, named `implement`.

      **`CHANGE_PARAM = "issue-<N>"` (normal case):** work `tasks.md` test-first
      (RED→GREEN→REFACTOR) in `<worktree>`, honoring the repo's documented conventions (CLAUDE.md /
      CONTRIBUTING / style guide — TDD, SOLID, whatever hard rules the repo documents), marking each
      task `- [x]` as completed and committing with focused messages. Append the TEST INSTRUCTION
      (step 3) and the implementer GUARDRAILS. **Also instruct it to message you (the lead) at each
      checkpoint push**, naming which `tasks.md` item(s) it just completed — not only in its final
      report — so you can post a GitHub comment (`gh issue comment <N> --body "✅ Implement:
      <task(s)> done, pushed \`<sha>\`."`) for each one as it arrives, giving the owner a live trail
      instead of one comment at the very end.

      **`CHANGE_PARAM = "none — type:tech-debt fast path"`:** there is no `tasks.md` — work directly
      from the issue's own body instead (`gh issue view <N> --json title,body`): its `## Direction`
      is the shape of the fix, its `## Acceptance criteria` states the behavior-preservation bar
      explicitly, and its `## Adjacent specified behavior (must be preserved)` section (if present)
      names existing `openspec/specs/**` requirements this surface touches — don't contradict them.
      Implement exactly that Direction, test-first wherever you touch anything non-trivial. **This
      is behavior-preserving** — append this explicit instruction on top of the TEST INSTRUCTION:
      *"If achieving the Direction cleanly would require changing any observable behavior (a public
      signature, an error contract, CLI/config/serialized output, or an existing test's asserted
      behavior), STOP and report the specific behavior delta instead of implementing it — do not
      silently make the change."* Append the implementer GUARDRAILS as normal (this teammate does
      NOT open the PR itself — you do, right below, since Team mode's lead can).

      Either way: wait for it to report and mark its task complete before moving on — nothing else
      can start yet. **Then, only for the tech-debt case, if step 2b was skipped** (no PR yet — this
      is always true the first time through for a `type:tech-debt` issue): open the draft PR
      yourself now, using step 2's full mechanics (existing-PR reuse check included) — there's a
      real commit to open it against. Do this before moving to step b, so CI starts running while
      the panel reviews.

   b. **Review — the panel this repo names, spawned together, every round.** **Read
      `spec-flow/WORKFLOWS.md` first** (via `${CLAUDE_PLUGIN_ROOT}/scripts/repo-config.sh
      instruction`'s directory, or `SPEC_FLOW_CONFIG_DIR` when set — the same file step 0's check
      already required). It states which lenses run, what counts as must-fix, and the round cap.
      **This plugin ships no default panel**: if you cannot read that file, stop and say so — do
      not fall back to a list of your own. The five below are what the seeding template proposes,
      not what the pipeline requires.

      Spawn the lenses that file names, all in one message so they run in parallel, each depending
      on Implement's task in the shared task list (so none can start early), each told to reply to
      you with **exactly** this JSON contract in its final message before marking its task
      complete — and nothing else:
      ```
      {"summary":"…","spec_conformance":"full|partial|failing","tests_ran":"policy|partial|degraded|none",
       "tests_detail":"the exact commands you ran",
       "findings":[{"id":"…","severity":"blocker|major|minor|nit","location":"…","rule":"…","problem":"…","fix":"…"}],
       "approve":true|false}
      ```
      `tests_ran` is relative to **the repo's policy**, never to a tier: `policy` = ran exactly what
      the policy names, plus the flagged set; `partial` = ran some of it; `degraded` = the policy's
      command exists but could not run, so something weaker ran instead; `none` = ran nothing while
      the policy named something. **Where the policy names nothing to run, running nothing is
      `policy`** — not `none` and not `degraded`. `tests_detail` names the exact commands, and is
      what step 5's PR body quotes.

      A sixth value, `unknown`, is **yours, not a lens's**: use it in step 5 when no panel result
      exists at all — a tech-debt breaker stop skipped the panel, or a lens went idle without
      reporting. Never assert compliance nobody checked; `none` is a claim about a run that
      happened, not a non-answer. Pair it with a `tests_detail` saying why nothing was assessed,
      the way `implement.workflow.js` does on the same three paths.

      Append **both the REVIEW GUARDRAILS and the TEST INSTRUCTION** (step 3) to every one of
      these. The spec lens is told by its agent file to run exactly what the TEST INSTRUCTION
      directs and nothing else, so a lens spawned without that line has no pointer to the policy
      and must guess a test command — the hardcoded-policy behavior this whole change removes. The
      five, named `spec`, `code-review`, `security-review`, `test-rigor`, `observability`:

      The bundled ones are backed by this plugin's own agent definitions (`agents/reviewer.md`,
      `code-reviewer.md`, `security-reviewer.md`, `test-rigor-reviewer.md`,
      `observability-reviewer.md`). Spawning by that agent type already applies its full mandate,
      process, and output contract as the teammate's system prompt, so every spawn prompt below
      only supplies the concrete runtime values — restating the mandate here would just be a second
      copy that could drift from the agent file. `code-reviewer`/`security-reviewer` need Skill-tool
      access to invoke the built-in `/code-review`/`/security-review` skills, which their agent
      files grant by omitting a restrictive `tools:` line (unlike `reviewer`'s Read/Bash/Grep/Glob):

      Spawn each with the identical prompt — *"Panel mode. worktree: `<worktree>`. base: `<base>`.
      change: `<CHANGE_PARAM>`. issue: #N. Follow your agent definition's process and output
      contract exactly (JSON only).\n`<TEST INSTRUCTION — step 3's stdout, verbatim>`"* — then the
      REVIEW GUARDRAILS after it. `<CHANGE_PARAM>` is exactly the value resolved at the top of
      this step (`"issue-<N>"`, or the tech-debt sentinel), never hardcoded as `issue-<N>` — that
      sentinel is what switches `reviewer` into its behavior-preservation mode (see
      `agents/reviewer.md`'s "Tech-debt fast path mode"); the other four lenses just treat it as
      informational context, same as any other diff-review run. Only the teammate name and backing
      agent type vary otherwise:

      The bundled lenses and their backing agents — use whichever subset this repo's file names,
      and resolve any agent it names that isn't here from `.claude/agents/` on equal terms:

      | Teammate | Agent |
      |---|---|
      | `spec` | `reviewer` |
      | `code-review` | `code-reviewer` |
      | `security-review` | `security-reviewer` |
      | `test-rigor` | `test-rigor-reviewer` |
      | `observability` | `observability-reviewer` |

      **A lens the repo names that you cannot resolve stops the run, by name.** Never drop it and
      never substitute another — the same refusal the pipeline gives for an unresolvable
      `SPEC_FLOW_DEVELOPER_AGENT`. A panel silently one lens short is worse than a stopped run.

      **If the repo's file states that no panel runs**, spawn nothing: skip this step and step c
      and d entirely, go straight to step e (Build), and in step 5's PR body say plainly that no
      review panel ran because that is this repo's policy. Never report an approval — nobody
      approved anything.

   c. **Merge and gate.** Once every review task is complete — or a teammate goes idle without
      reporting, which counts exactly like a missing lens, never silently dropped from the vote —
      parse each teammate's JSON from its message to you. Merge `findings` across every lens the
      repo's file named.
      **Before computing `mustFix`: any lens that reported `approve: false` with NO blocker/major
      finding among what it reported** (the spec lens can do this — it requires
      `spec_conformance: "full"` to approve, so a `"partial"` verdict alone sets `approve: false`
      with nothing to point at; a lens can also decline with only minor/nit findings, which is
      just as unexplained since those never enter `mustFix` on their own) —
      **synthesize a finding for it** so its non-approval has something to work from instead of
      silently reaching the round cap with no must-fix findings and no visible reason: `{id:
      "unexplained-<lens>", severity: "major", location: "(<lens> lens report)", rule:
      "unexplained-non-approval", problem: "<lens> lens returned approve=false with no findings
      (summary: <its summary>)", fix: "Re-review and either approve, or report a specific blocking
      finding."}`. Add these to `findings` alongside whatever each lens actually reported, THEN
      compute `mustFix` = every finding at the severities **this repo's `WORKFLOWS.md` names as
      must-fix** — and synthesize the `unexplained-non-approval` finding at the FIRST of those
      severities, never a fixed `major`: on a blockers-only gate a `major` would never enter
      `mustFix`, leaving the non-approval with nothing to act on (including synthesized ones); the seeded default is `blocker` plus `major`.
      **Approve** only if every lens the repo named reported AND every `approve` is `true` AND
      `mustFix` is empty. Never count a lens the repo removed as missing, and never count a lens
      that returned no result as an approval.
      Either way, post the round's result as a comment:
      `gh issue comment <N> --body "✅ Review panel approved (round <R>)."` or
      `gh issue comment <N> --body "🔁 Review round <R>: <M> must-fix finding(s), fixing…"`.

   d. **Fix — bounded by this repo's round cap.** The cap comes from `WORKFLOWS.md` (the seeded
      default is 3). Not approved and a round remains: `mustFix` non-empty → message the `tdd-developer` teammate (respawn it, named `fix-N`,
      if it already shut down) with the consolidated `mustFix` list (severity, location, rule,
      problem, suggested fix for each), the TEST INSTRUCTION, and the implementer GUARDRAILS —
      resolve each, test-first where behavior changes, commit, push at checkpoints. Then go back
      to step b for a fresh review round. `mustFix` empty but a lens is simply missing → skip
      straight back to step b, nothing to fix yet. **Not approved, `mustFix` empty AND no lens
      missing** — reachable when a lens declines over something this repo's gate does not treat as
      must-fix — **stop the loop**: another round has the same inputs and produces the same verdict,
      and a fix teammate would be handed an empty list. Take step 5's gate failure path and tell the
      owner a lens declined on a non-blocking finding, so it needs their call. **If any implementer's report contains
      `SPEC-DEFECT:`, stop the loop immediately** — the approved spec is what's wrong, more rounds
      cannot fix it (the one change that would resolve the finding is the one GUARDRAILS forbids),
      and only the owner can change it. Take step 5's gate failure path with that report as the
      residual finding, and tell the owner plainly that this is a spec defect, not an
      implementation failure: the next step is `/spec-flow:activate <N>` to redirect at Seam 1, not
      a re-run of implement. At the repo's round cap with still no approval: stop,
      collect the outstanding `mustFix` findings (plus which lens(es) never reported) as
      **residual**, and skip straight to step 5's **gate** — do not run Build/Polish on a tree
      that's going through another round regardless. The gate fails on this path, so step 5 stops
      there; it does not mark the PR ready.

   e. **Build** (only once approved). Spawn `build-engineer`, named `build`: get format/lint/build
      clean in `<worktree>` — *"Discover and run the repo's format, lint, and build steps
      (examples: Rust `cargo fmt` → `cargo clippy --all-targets -- -D warnings` → `cargo build`;
      Node the repo's lint+build scripts; Gradle `./gradlew spotlessApply build`; Go `gofmt -l .` →
      `go vet ./...` → `go build ./...` — those are illustrations, not a list to work through, and a
      repo may configure none of them). Read the repo's own policy named in the line below before
      you start: in some repos the local gate it states IS the format and lint gate, and may be the
      only thing there is to run here. Follow it where it speaks to your work, and never invent a
      build step it does not name. Resolve formatting/lint/build issues WITHOUT changing behavior,
      commit, push. Return the final format/lint/build status."* Append the **TEST INSTRUCTION**
      (step 3) and then the implementer GUARDRAILS. When it reports, comment:
      `gh issue comment <N> --body "🔧 Build clean."`.

      Step 3's list covers the teammates that run **tests**; `build-engineer` is listed separately
      here because it gets the same pointer for a different reason — its format and lint gate is
      whatever the repo's policy names, and in a repo whose policy defines the local gate as lint
      alone, an agent that cannot read that policy cannot run the gate at all.

   f. **Polish.** Spawn a `tdd-developer`-type teammate, named `polish`. Open the prompt by naming
      the target: when `CHANGE_PARAM` is a real change, *"Final documentation polish for OpenSpec
      change `issue-<N>` in `<worktree>`"*; on a `type:tech-debt` issue `CHANGE_PARAM` is the
      sentinel, so name the issue instead — *"Final documentation polish for issue #<N> in
      `<worktree>`"* — never the sentinel itself, which points at a change that does not exist.
      Then continue, in the same prompt: *"Ensure new modules/behaviors are
      documented consistently with the repo's conventions (module/responsibility comments,
      architecture/index docs, doc comments on public items). If this change alters user-facing
      behavior (public API, CLI, config, how the service runs), update the repo's user-facing docs
      accordingly (README, a docs/ tree, an mdBook, a docs site) — keep pages/examples current. No
      user-facing docs or no user-facing surface → skip and say so. Documentation/comment edits
      only; commit, push. Return a one-line note on what you documented."* Append the implementer
      GUARDRAILS. When it reports, comment: `gh issue comment <N> --body "📚 Docs polished."`.

   g. **Shut down the team.** Once Build and Polish report back, ask every teammate still running
      to shut down — don't leave idle teammates running into step 5 or the owner's next round.

   If the panel never approved within the bounded loop, skip straight from step d to step 5 —
   no Build, no Polish. Shut down any teammates still running before you do (same as step g);
   leave the PR a draft and surface the residual findings to the owner.

   **Workflow mode (fallback).** Invoke the `Workflow` tool with the script bundled in this
   plugin and pass `args`:
   ```json
   {
     "scriptPath": "${CLAUDE_PLUGIN_ROOT}/skills/implement/implement.workflow.js",
     "args": {
       "worktree": "<abs path — $(git rev-parse --show-toplevel), Claude Code's own isolated checkout for this session>",
       "change":   "<CHANGE_PARAM — resolved at the top of this step: \"issue-<N>\", or the tech-debt sentinel>",
       "issue":    <N>,
       "base":     "origin/<DEFAULT_BR>",
       "buildSystem": "auto",
       "breaker":  "<BREAKER_PARAM — \"ask\" (default) or \"revert\" on the tech-debt path; \"off\" on the normal path>",
       "testInstruction": "<the TEST INSTRUCTION — step 3's stdout, pasted verbatim, on one line>",
       "panel": [
         {"label": "spec",             "agentType": "reviewer"},
         {"label": "code-review",      "agentType": "code-reviewer"},
         {"label": "security-review",  "agentType": "security-reviewer"},
         {"label": "test-rigor",       "agentType": "test-rigor-reviewer"},
         {"label": "observability",    "agentType": "observability-reviewer"}
       ],
       "gate": {"mustFixSeverities": ["blocker", "major"], "maxRounds": 3}
     }
   }
   ```
   **`panel` and `gate` are derived by YOU from this repo's `spec-flow/WORKFLOWS.md`** — the values
   shown above are what the seeding template proposes, not a default to paste blind. Read that file
   and write the lenses it names, in its order, with the gate thresholds it states. A repo whose
   policy is that no automated panel runs gets `"panel": []`, which is valid and means exactly
   that: the script spawns nothing, runs no fix loop, goes straight to Build and Polish, and
   reports that no panel ran rather than an approval. Both are **required**: the script cannot read
   files, so it throws rather than substituting a panel of its own — the same contract as
   `testInstruction`, and for the same reason. This is what stops Workflow mode and Team mode
   holding two copies of the policy that drift apart.

   `testInstruction` is **required**. The script cannot read files or the environment, so this is
   the only way the repo's policy reaches Workflow mode, and the script throws rather than
   substituting a default of its own. Paste step 3's stdout exactly as printed: it is one line, and
   `repo-config.sh` refuses to emit the line at all unless the resolved path is free of control
   characters and of the metacharacters that terminate or interpolate into a JS template literal, a
   JSON string, or a shell command — so pasting it as-is into the JSON value below is safe. A space
   in the path is fine and does not need quoting here.
   The script runs the identical sequence as Team mode above — tdd-developer implements test-first
   → the same panel reviews the diff in parallel, each lens the same prompt and JSON
   contract as Team mode's step b → the same bounded (3-round) fix loop → build-engineer gets the
   build clean → docs polish — as a scripted `agent()`/`parallel()` loop instead of you reasoning
   through it as a team lead. It returns a summary object (`tests_ran`, `tests_detail`, `spec_conformance`,
   `approved`, `review_rounds`, `residual_findings`, `non_blocking_findings`, `review_summary`,
   `polish`) — use those fields directly for step 5, instead of the ones you tracked yourself in
   Team mode. `base`/`buildSystem` have the same meaning as Team mode's `base` and the Build
   step's hint.

   **Progress comments are coarser in this mode.** The script has no hook back out to you
   mid-run, so you only know what it did once it returns — you can't relay per-task or per-round
   comments the way Team mode does. When it returns, post one comment summarizing the whole
   pass: `gh issue comment <N> --body "✅ Implemented, reviewed (round <review_rounds>), and
   built — see PR for details."` (or, if `approved` is false, the residual findings instead). Say
   so plainly if the owner asks why this run's issue history is sparser than a Team-mode run's.

5. **Mark the PR ready and report.**

   **Gate — check this first, before any command below.** Every path into this step must pass it,
   including the "skip straight to step 5" route out of step 4d. Answer all four:

   - Did the review panel approve? (Or, on the docs fast path, did step 4c report done? Or does
     this repo's `WORKFLOWS.md` state that **no panel runs** — in which case there is no approval
     to wait for and this question does not apply. Workflow mode reports that as
     `panel_ran: false`.)
   - Did the run reach here without a refactor circuit breaker trip, in Implement or in any fix
     round?
   - Did every agent this run depended on actually return a result, rather than dying or being
     skipped?
   - Do you have a real `<PR>` number? On a fast path the PR is opened after step 2a ran, so
     re-resolve it with 2a's `gh pr list --head "$BR"` command before answering. If it is still
     empty, no PR exists — stop and say so rather than running the commands below against nothing.
   (CI is not knowable here — the push it reports on happens below. The CI check further down
   enforces the same rule at the point where the answer exists.)

   **If any answer is no, do not run the commands below.** Instead: add `needs-attention`
   (`gh issue edit <N> --add-label needs-attention`) with a comment naming exactly what is
   unresolved — first line prefixed `🆘 Needs attention:`, per `agents/issue-manager.md` — keep
   `agent:active` and `status:in-progress` as they are, and stop. **If a PR exists**, also leave it
   a draft and write the residual findings into its body. **If none exists** — a fast-path run that
   halted before its PR was ever opened, which is exactly what question 4 catches — put the
   residual findings in that issue comment instead; it is the only place they can land. Never mark a red or unapproved PR ready, and never
   merge one, whatever `merge-on-green` or `.spec-flow/owner-instructions` say. Those authorize
   crossing Seam 2 on a *finished* run; they do not authorize skipping the panel.

   When the gate passes, finalize the already-open draft PR
   (outward-facing — done here in this session, narrated). Re-resolve `$BR` fresh here — cheap, and
   this may be a separate Bash call from step 2's, which wouldn't have carried it over:
   ```bash
   BR=$(git rev-parse --abbrev-ref HEAD)
   git -C <worktree> push origin "$BR"                     # ensure the final state is pushed
   ```
   **Before marking ready, check once whether CI already resolved this exact push** — the panel
   review in step 4 often takes long enough that CI has already finished on this SHA, and pushing a
   guess-fix and waiting another 20-30 minutes for CI to confirm it is exactly the round-trip the
   flagged-test mechanism exists to avoid. This is a single check, not a wait or a poll loop — if CI hasn't
   finished yet, move on:
   ```bash
   HEAD_SHA=$(git -C <worktree> rev-parse HEAD)
   gh run list --branch "$BR" --json databaseId,status,conclusion,headSha --limit 10 \
     --jq ".[] | select(.headSha == \"$HEAD_SHA\")"
   ```
   **Not found, or `status` isn't `completed`** → CI hasn't reported on this SHA yet; proceed to
   `gh pr ready` below as normal — the existing owner-notices-red path still covers it later.
   **`conclusion` is `failure`** → run `/spec-flow:sync-ci <N>`'s own mechanics right here (its
   SKILL.md steps 3-5: download the `spec-flow-failures` artifact, append the ids to
   `.spec-flow/flagged-tests`). If it produced no artifact (a build/lint break, not a test
   failure), skip the fix round below and surface it as a residual finding instead — don't guess
   at a fix for something that isn't a flagged test. Otherwise, run **one bounded fix round**:
   respawn `tdd-developer` (named `fix-ci`) with the newly flagged test id(s), the TEST
   INSTRUCTION (step 3 — which already runs `.spec-flow/flagged-tests` locally), and the
   implementer GUARDRAILS, and explicitly instruct it to **confirm the flagged test(s) pass
   locally before pushing** — never push a fix for a known failure without reproducing and
   clearing it locally first. Once it reports back and pushes, repeat this same single check
   against the new HEAD sha once more — this is one bounded pass, not a loop back into step 4's
   panel. **If CI is still red after that pass, or the break was a build/lint break surfaced as a
   residual finding above, stop here and take the gate's failure path** (leave the PR a draft,
   residual findings in its body, `needs-attention`, keep `agent:active` and
   `status:in-progress`). Do not run the commands below. "Proceed either way" would mark a red PR
   ready, which the gate forbids.
   ```bash
   gh pr ready <PR>                                        # un-draft — ready for your review (Seam 2)
   gh pr edit <PR> --body "Closes #<N>

   <the review_summary from step 4 (tracked yourself in Team mode, or the script's return value in Workflow mode), INCLUDING one line quoting tests_detail — the exact commands that ran — and nothing about tiers, CI, or what was deliberately not run>

   <if non_blocking_findings is non-empty, a 'Surfaced, non-blocking' section listing each one — these never blocked approval but the owner should still see them at Seam 2>"
   gh issue edit <N> --remove-label status:in-progress --add-label status:in-review
   gh pr view <PR> --json url --jq .url
   ```
   Use the printed URL as `<PR_URL>` below — same convention as `<DEFAULT_BR>`/`<PR>` above,
   a literal value from this output, not a shell variable carried across separate Bash calls:

   **If `SPEC_FLOW_SEAM_VIEW=explain`** (set once, repo-wide, by `/spec-flow:setup` — see **Seam
   visualization** in `docs/workflow.md`; unset or `terminal` skips this entirely), resolve the
   standalone `dev-skills` plugin's installed root the same way `activate` step 7 does:
   ```bash
   EXPLAIN_ROOT=$(claude plugin list --json 2>/dev/null | jq -r '
     [.[] | select(.id | startswith("dev-skills@")) | select(.enabled)]
     | sort_by(.installedAt) | last.installPath // empty')
   ```
   `EXPLAIN_ROOT` empty → skip straight to **Otherwise** below, and mention once, in passing, that
   the seam-view preference is set but `dev-skills` isn't available here. Otherwise:

   **First, write a real explanation for each changed file — not commit history, an actual
   account of what changed.** `dev-skills`' own `--blame` can only quote historical commit
   messages; the only way the explain view's explanation pane says something genuinely useful is
   a caller that has actually read the diff supplying that content itself — see `--explain-map` in
   `dev-skills`'s `skills/ide-explain/SKILL.md`. You already have this understanding from driving
   the implementation and the review panel — this is externalizing what you already know, not a
   fresh re-read of the diff. For every file `git diff --name-only "$(git merge-base HEAD
   origin/<DEFAULT_BR>)" "$BR"` lists, write 1–3 concrete sentences (what changed in this file,
   and why) into `.spec-flow/explain-map.json` as `{"path": "explanation", ...}` — skip a file only
   if it's genuinely mechanical with nothing to say (a lockfile regeneration, pure formatting).
   Then generate a "what changed" explain view alongside the PR before posting the comment below:
   ```bash
   "$EXPLAIN_ROOT/skills/ide-explain/scripts/generate-explain.py" \
     --diff --base "$(git merge-base HEAD origin/<DEFAULT_BR>)" --head "$BR" \
     --change "openspec/changes/issue-<N>" \
     --explain-map .spec-flow/explain-map.json \
     --title "issue-<N>" --subtitle "PR #<PR> — what changed" --out <path>
   ```
   (omit `--change` if this issue never generated an OpenSpec change — the docs-fast-path/tech-debt
   case; omit `--explain-map` only if it ended up empty, e.g. every file was genuinely mechanical).
   Never pass `--open` — same display constraint as `activate`, see `dev-skills`'s own
   `skills/ide-explain/SKILL.md`.
   ```bash
   gh issue comment <N> --body "👀 PR #<PR> ready for your review: <PR_URL>

   🗂️ Explain (what changed): <explain path> — open <explain path>"
   ```
   **Otherwise** (unset, `terminal`, or `dev-skills` unavailable):
   ```bash
   gh issue comment <N> --body "👀 PR #<PR> ready for your review: <PR_URL>"
   ```
   **Always the full URL, never a bare PR number** — `<PR_URL>` resolved above, both in this
   comment and whenever you tell the owner directly (in this conversation, not just GitHub) that
   the PR is ready for review, further down in this step.
   Check whether to auto-merge — the `merge-on-green` label (checked fresh, not assumed) or
   `.spec-flow/owner-instructions` (also read fresh, not from memory of the spawn prompt) saying so:
   ```bash
   gh issue view <N> --json labels --jq '[.labels[].name] | any(. == "merge-on-green")'
   ```
   **If that printed `true`, or the file says so — don't stop here:**
   ```bash
   gh pr checks <PR> --required --watch
   ```
   `--required` scopes this to the PR's *required* status checks, not every check reported;
   `--watch` blocks until they finish and exits non-zero if any failed — treat a non-zero exit as
   a stop-and-surface-to-the-owner case, not a merge. **If it reports no required checks at all,
   don't conclude that immediately** — `gh` can't tell "nothing's configured" apart from "required
   checks are configured but haven't posted a check run yet" (e.g. right after this session's own
   push, before CI has started). Wait a short interval and re-check once before deciding; still
   none → **that's a configuration gap, not a green light** — refuse to auto-merge and tell the
   owner (e.g. "no required checks configured — configure branch protection for auto-merge to be
   safe, or merge this one manually"). Only once required checks are confirmed to have actually run
   and passed:
   ```bash
   gh pr merge <PR> --squash --delete-branch=false
   gh issue comment <N> --body "Merged automatically (merge-on-green, CI green)."
   ```
   and continue straight to `/spec-flow:finalize <N>` yourself — there's no review round to wait
   on. **Absent either trigger, this is always the stop:** give the owner the full PR URL
   (`<PR_URL>` resolved above — never just `#<PR>` or "the PR") for GitHub review (Seam 2) and
   wait. When they leave comments, the next step is `/spec-flow:address <N>`;
   after they squash-merge, `/spec-flow:finalize <N>`.

## Rules

- **Never merge, never push to `main` — by default.** This skill pushes only the issue branch,
  opens a *draft* PR, and later marks it ready. It merges on its own only when the `merge-on-green`
  label is set, or `.spec-flow/owner-instructions` explicitly said to — and even then only after
  the PR's required checks report green, via a squash-merge of that one PR — never a direct push
  to `main`.
- **If a PR already exists for the branch (re-run), reuse it rather than erroring.** Common after
  a resumed/interrupted run, residual findings sent back for another pass, or the owner asking for
  another round.
- **Draft until approved.** The PR opens as a draft so CI runs during implementation; mark it ready
  only after the review panel approves. If the panel can't reach `approve` within the bounded fix
  loop, leave the PR a draft and surface the residual findings to the owner — never mark a
  red/unapproved PR ready.
- **The PR body states what actually ran, quoting `tests_detail` — never a tier, and never a policy
  of its own.** Copy the commands the panel reported. Never claim a run that did not happen, and
  never add a line about what was deliberately not run: the repo's policy already settles that, and
  in a repo whose policy names nothing to run locally, "nothing ran locally" is the honest and
  complete answer.
- All code work happens in the worktree; this session only orchestrates, pushes, and manages the PR.
- When you cite an issue or PR, always write it as `<number>: <title>`, on its own line with a `-`
  prefix — never a bare number, and never several run together inline in a sentence.