implement · diff
git:20260901.bb438e2 to git:20260901.926359e
85 added, 20 removed. Audit A to A.
---
name: implement
- description: Implement an approved issue — run tdd-developer → 5-lens 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-pm (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.
+ 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-pm (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-pm`, 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 five lenses, same rules, no team. **Invoking this skill is the owner's explicit
+ 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 five
+ 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 five-lens panel, the fix loop, Build, Polish — entirely and run this
+ 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 five-lens panel — every other issue, however small, still
+ 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 five lenses and the same merge/approve/fix-loop rules,
+ (`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`, and `review_summary` as you go —
+ `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 — five lenses, spawned together, every round.** Once Implement's task is complete,
- spawn all five teammates 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
+ 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`:
- All five are backed by this plugin's own agent definitions (`agents/reviewer.md`,
+ 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 all five.
+ 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 `blocker`/`major` finding (including synthesized ones). **Approve**
- only if every one of the five reported AND every `approve` is `true` AND `mustFix` is empty.
+ 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, max 3 rounds.** Not approved and a round remains (start at round 1, cap at
- 3): `mustFix` non-empty → message the `tdd-developer` teammate (respawn it, named `fix-N`,
+ 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. At round 3 with still no approval: stop,
+ 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>"
+ "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 five-lens panel reviews the diff in parallel, each lens the same prompt and JSON
+ → 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?)
+ - 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-pm.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.