secondmate · git:20260924.bedbc73 · 2026-09-24 · sha256 1f8f41e982eb19d3

secondmate git:20260924.bedbc73A

Immutable. This exact content never changes and is served at /api/v1/blob/1f8f41e982eb19d3.

---
name: secondmate
description: >
  Maker/checker loop for shipping a verified code change: triage → isolated
  worktree → maker implements → cross-model edit-locked checker emits a
  {verdict} envelope → verify-gate → durable human hold → integrate. Use when
  the user wants a change made AND independently verified before it lands, asks
  to "run the maker/checker loop", "review with a cross-model checker", gate a
  merge, or run a long iterative edit→check loop safely. Also exposes a read-only
  reasoning one-shot for root-cause/triage/plan-review. Skip for trivial one-shot
  edits or read-only questions.
---

# secondmate — maker/checker loop hardening

**Setup:** run `/secondmate-doctor` once — it detects and installs the harness + companions (herdr, ponytail,
adhd) and reports what's missing; the human only approves each action. The `/loop-task` command ships
with this plugin. Cross-model checking
additionally needs a second model family + credentials, which only the human can supply.

Scripts live in `${CLAUDE_PLUGIN_ROOT}/bin/` (a `${CLAUDE_PLUGIN_ROOT}/bin/reason.sh` etc.).
If `$CLAUDE_PLUGIN_ROOT` is unset in your shell, resolve it once: it is this plugin's install dir
(under `~/.claude/plugins/marketplaces/secondmate` or your skills dir). All scripts carry `--selfcheck`/
`selfcheck` and are harness-neutral; the checker/reasoning harness + model are set by env
(see the Config section of the plugin README). You (the supervisor) invoke these — the user does not.

## Roles
- **Supervisor** (you) — triage, adjudicate, gate, integrate. Never write project code yourself.
- **Maker** — implements in an isolated worktree (e.g. a Claude `/loop-task`).
- **Checker** — a *different* model, edit-locked, that reviews the diff and emits a verdict. Its blind
  spots must not correlate with the maker's, so run a different model family than the maker. If no external
  harness is installed, the fallback is a different-model Claude sub-agent (same vendor, weaker, but maker is
  still not the checker) — see the Check step.

**Session guard (sleep prevention):** `caffeinate-guard.sh` is a SESSION-SCOPED process, NOT per-task.
- Primary: call `start` ONCE at the very beginning of a work session/batch (before triaging the first task)
- Also safe (though redundant): you may call `start` again at each task's Spawn step since it is idempotent
- Call `stop` ONCE yourself, after you have confirmed EVERY task/worktree in that batch has been torn down
- Never call `stop` inside per-task teardown — sibling tasks may still be running

## Plan Committee (pre-triage, unconditionally for every task)

Before triaging, run the planning committee to gather independent perspectives from multiple models.

**0a — adhd subagent (Claude cognitive frames, you run this):**
Invoke `/adhd` as a Claude sub-agent; save its winning branch to `.secondmate/planning/adhd.md`.

**0b — multi-model planners (parallel, 6 models):**
```
${CLAUDE_PLUGIN_ROOT}/bin/plan-committee.sh --task "<task description>" [--timeout 300]
```
Spawns 6 headless pi planners in parallel (DeepSeek-R1 → failure modes; Qwen3-Next-80B → architecture;
Qwen3-Coder-Next → implementation; Kimi K3 (bedrock cross-region inference profile: global.moonshotai.kimi-k3) → holistic risk; Mistral-Large-3 → security;
GLM-5 → requirements/product). Outputs: `.secondmate/planning/<label>.md`.

**0c — synthesize (you, the supervisor):**
Read all `.secondmate/planning/` files in order:

1. **Collect probes.** Every planner ends with a `### Probes for Supervisor` section — 2-3 questions
   it raised but could not answer (planners are headless, no tools). Read the six `<label>.md` files
   (skip `audit.jsonl` and any stale outputs). List all probes before writing anything.

2. **Answer every probe.** For each probe, resolve it using your session context: read the relevant
   file, grep the codebase, check the package manifest. If a probe requires a file read, do it now.
   - **If a probe is answerable from the repository:** answer it and record the evidence.
   - **If a probe requires a business, legal, or product decision not in the repo:** record it as an
     OPEN DECISION, escalate to the human before proceeding, and do not invent an answer.
   A probe left unanswered without escalation is a supervisor failure — the maker must not receive it.

3. **Extract signal, discard noise.** Weak analysis, duplicate findings, and generic advice go.
   Keep only concrete, task-specific findings from each dimension.

4. **Write ONE consolidated plan.** Bind the probe answers into the plan so the maker gets a
   spec that is already resolved — no open questions, no "figure it out" gaps. OPEN DECISIONs that
   were escalated are excluded from the plan until the human answers them.

This is the spec the maker receives.

**0d — route the maker** (after step 2 Spawn has created `<wt>`):
- **Complex** (needs judgment mid-task, MCP tools, ambiguous sub-steps) → Claude maker:
  After step 2 Spawn creates `<wt>`, start the Claude maker directly on the root_pane from `herdr worktree create`:
  ```bash
  herdr agent start sm-<task-id> --kind claude --pane <root_pane_id> -- --permission-mode auto || { echo "herdr agent start failed — abort" >&2; exit 1; }
  herdr agent prompt sm-<task-id> "Implement: <goal>. You are the maker — write the code, run tests, commit to this worktree, then reply DONE.  *Before replying DONE, write/update the round-state handoff file* (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.Do NOT invoke /loop-task or secondmate; the supervisor owns the checker loop.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<goal>")" --wait --timeout 600000
  ```
  The root_pane comes from `.result.root_pane.pane_id` of the `herdr worktree create` call. No split needed since the root_pane's cwd is already the worktree. Guard on the agent name before prompting — if the agent fails to start, abort rather than routing to a stale agent. Same `<task-id>` slug as the worktree branch. Give the goal + key constraints; Claude's own reasoning resolves the how — do not pre-specify steps that the maker's thinking can figure out.
- **Simple** (well-specified, pure code, no external deps) → pi maker via herdr (when `HERDR_ENV=1`):
  ```bash
  # Prerequisite: step 2 must have called mark-maker.sh after herdr worktree create (see step 2 for the full sequence)
  # agent name is TASK-SCOPED (sm-pi-<task-id>) — never a shared global name
  herdr agent start sm-pi-<task-id> --kind pi --pane <root_pane_id> \
    -- --provider amazon-bedrock --model qwen.qwen3-coder-next --thinking medium --extension "${CLAUDE_PLUGIN_ROOT}/bin/scope-guard-extension.ts"
  herdr agent prompt sm-pi-<task-id> "<plan> Before replying DONE, write/update the round-state handoff file (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<plan>")" --wait --timeout 600000
  ```
  `<root_pane_id>` comes from `.result.root_pane.pane_id` of the `herdr worktree create` call (step 2), and the
  worktree **must have been marked** by calling `${CLAUDE_PLUGIN_ROOT}/bin/mark-maker.sh --cwd <wt>` BEFORE
  starting this agent (finding #5 fix) — otherwise scope-guard.py won't activate and the maker can access anything.
  `<task-id>` is the same slug used in the worktree branch (e.g. `add-version-flag`). A task-scoped name
  prevents loop-back fix plans from being routed to a stale agent in a different worktree.
  Do NOT use `herdr-pane.sh spawn` here — without --pane, it splits from the caller's current pane (potentially in a different workspace); with --pane, it splits from the supplied pane's workspace.
  Use `--thinking medium` (not `off`) — Qwen's reasoning catches edge cases (null guards, trap safety,
  portability) that pure token prediction misses. Use `--thinking high` for security-sensitive or complex logic.
  Pi runs as a lifecycle-tracked herdr agent: if `blocked` (approval/question UI), inspect `herdr agent get/read`
  before deciding what to send — do not advance to Check while the maker is blocked. If `agent_prompt_stalled`
  (agent did not respond to the prompt within 5s), re-inspect agent state before retrying.
  Maker output is always read from `git -C <wt> diff`, not pi's terminal.
  If `HERDR_ENV` is not 1, fall back to headless:
  `cd <wt> && run-round.sh --label sm-pi-<task-id> -- pi --provider amazon-bedrock --model qwen.qwen3-coder-next --thinking medium --extension "${CLAUDE_PLUGIN_ROOT}/bin/scope-guard-extension.ts" -p "<plan> Before replying DONE, write/update the round-state handoff file (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<plan>")"`

  **Plan format — intent + constraints, not a recipe.** The maker has `--thinking medium/high`; let it reason.
  A good plan gives:
  - **What** to achieve (goal + success condition the checker will verify)
  - **Key constraints** (must not break X, must handle Y edge case, must be idempotent)
  - **Scope boundary** (what's in, what's explicitly out of this increment)

  A good plan does NOT give:
  - Exact file paths or line numbers (the maker reads the repo)
  - Step-by-step execution order (the maker's thinking handles this)
  - Every error-handling case spelled out (that's what `--thinking` is for)

  Pre-specifying every detail replaces the maker's reasoning with yours, which is weaker and wastes the model's thinking budget. The checker is the safety net — trust it.

  Note: routing happens at step 2 (Spawn). Steps 0a–0c produce the plan; steps 1–2 create the worktree; step 0d's maker command runs in that worktree.

After either maker path completes, **always proceed to step 4 (Check)** — same pi checker regardless of which maker ran:
`launch-checker.sh --addendum-text "..." --diff-base <base> --repo <wt> -- -p "review the change"` → `verdict.py`.
Checker model: `global.openai.gpt-5.6-terra` (default `SM_CHECKER_MODEL`). Maker ≠ checker invariant holds regardless of which maker path is chosen.

## The loop (run these yourself; the user invokes nothing)

1. **Triage** — classify the task `ship` (produces a diff) vs `scout` (report only; skip the checker and
   the gate), and a rigor tier: `full` (checker + verify-gate + human hold) or `fast` (tests + gate only).
   For a reasoning-heavy question with no tools needed (root-cause, triage, plan review, pre-mortem),
   delegate a one-shot:
   `${CLAUDE_PLUGIN_ROOT}/bin/reason.sh [--model r1|gpt] [--context <file>] "question"` — read its answer, decide.

2. **Spawn** — isolate the maker. Two paths:
   - **Headless / not in herdr:** `read wt branch < <(${CLAUDE_PLUGIN_ROOT}/bin/new-worktree.sh --repo <repo> --task <task-id>)` — never the primary checkout.
     (Already marks the worktree via `mark-maker.sh` internally.) **Call `${CLAUDE_PLUGIN_ROOT}/bin/caffeinate-guard.sh start` once per session/batch** to prevent system sleep during all tasks in that batch — idempotent, safe to call per-task.
   - **In herdr (`HERDR_ENV=1`):** 
     ```bash
     result=$(herdr worktree create --cwd <repo> --branch sm/<task-id> --base HEAD --label sm-<task-id> --no-focus)
     wt=$(echo "$result" | jq -r '.result.worktree.path')
     root_pane=$(echo "$result" | jq -r '.result.root_pane.pane_id')
     ${CLAUDE_PLUGIN_ROOT}/bin/mark-maker.sh --cwd "$wt"  # REQUIRED: mark before starting the maker
     ${CLAUDE_PLUGIN_ROOT}/bin/sync-worktree-skills.sh --primary <repo> --worktree "$wt"  # copy gitignored .claude/skills/ into the new worktree
     ${CLAUDE_PLUGIN_ROOT}/bin/caffeinate-guard.sh start  # prevent sleep during session execution
     ```
     Creates the git worktree AND a herdr workspace/tab/pane in one call. **Must call mark-maker.sh** before starting
     any maker agent in this worktree (finding #5 fix) — otherwise scope-guard.py won't activate.
     **Must call `${CLAUDE_PLUGIN_ROOT}/bin/caffeinate-guard.sh start`** to prevent system sleep during the session (default 8-hour ceiling via -t).
     **Must call `sync-worktree-skills.sh`** too: project-local `.claude/skills/` is commonly gitignored, so neither `git worktree add` (used internally by `herdr worktree create`) nor `herdr worktree create` itself ever brings it into a fresh worktree on their own.

3. **Guard the round** — wrap each maker/checker invocation and track loop health:
   - `${CLAUDE_PLUGIN_ROOT}/bin/run-round.sh --label <id> -- <cmd>` (wall-clock timeout, idle watchdog, audit record even on kill).
   - `${CLAUDE_PLUGIN_ROOT}/bin/loop-guard.sh action --key "<canonical diff/action>"` and
     `${CLAUDE_PLUGIN_ROOT}/bin/loop-guard.sh round` (per-run round cap + global spawn cap; exhaustion reports `budget-limited`, never success).
     `loop-guard.sh reset` on a new task or human interjection.
   **Exit codes for `loop-guard.sh action`:**
     - `0` = ok, continue (n < 3; silent, no output).
     - `5` = restart recommended (3 <= n < ABORT_REPEATS): the supervisor should kill the maker's herdr agent (e.g., `herdr agent stop sm-<task-id>` or terminate the agent in the pane) and start a fresh one with the same task-scoped name, prompting it with the `round-state.md` handoff content.
     - `3` = hard abort (n >= ABORT_REPEATS): escalate to human intervention.
     - `4` = budget exhausted (from `round` subcommand): unchanged behavior.
     **Note:** `loop-guard.sh` is a pure read-only signal generator; it never kills or restarts anything.
     The ACTOR is the supervisor, who interprets the exit code and takes action accordingly.

4. **Check** — after the maker commits, trim bulky logs then run the cross-model checker:
   - **Strict rule: when `HERDR_ENV=1`, the checker MUST run in a visible herdr pane — headless is prohibited.** Use the "Checker pane" recipe under "Visible orchestration in herdr" below, not the plain invocation shown in this step. The plain `launch-checker.sh` call below is ONLY for when `HERDR_ENV` is not `1`, or `${CLAUDE_PLUGIN_ROOT}/bin/herdr-pane.sh check` fails.
   - `${CLAUDE_PLUGIN_ROOT}/bin/prune-output.sh` on big command output before feeding it in.
   - `${CLAUDE_PLUGIN_ROOT}/bin/launch-checker.sh --addendum-text "TASK/SPEC/HAMMER/INVARIANTS ..." --diff-base <base-ref> --repo <wt> --live-text "<what changed this round + what to focus on>" -- -p "review the change described in the LIVE block"`
     — edit-locked (`--exclude-tools edit,write`), verdict-envelope injected automatically. **Layered, freshest LAST:** (1) static
     eval-tuned discipline, (2) optional lenses, (3) envelope, (4) your **standing** per-task addendum (TASK/SPEC/HAMMER/INVARIANTS —
     stable across the task's rounds), (5) the **LIVE** layer for THIS round. The addendum is the *standard*; the live layer rides *on top* as fresh, current context.
   - **Keep the checker alive, not stale — regenerate the LIVE layer every round.** `--diff-base <ref>` auto-injects the real
     `git diff <ref>..HEAD` (pruned) so the checker always sees exactly what changed NOW — never a pasted, stale diff. After each round,
     save the checker's output to `$SM_LAST_VERDICT` (default `$SM_LOOP_STATE/last-verdict.md`); `launch-checker.sh` auto-injects it the
     next round as "prior verdict — resolve or re-confirm", giving the checker round-to-round memory. Put this round's one-line focus
     (what you just changed, what to hammer now) in `--live-text`. Set the addendum ONCE per task; refresh `--diff-base` / `--live-text` / the saved verdict EACH round.
   - **Pick specialized lenses by task — load only what the diff touches.** Beyond the base correctness
     discipline, choose the role(s) that fit: `redteam` (security-sensitive), `qa` (tests/behavior),
     `reverse-engineer` (unfamiliar/obfuscated/third-party code), `research` (novel/uncertain design, possible
     hallucinated APIs). Read that role's router `${CLAUDE_PLUGIN_ROOT}/bin/lenses/<role>/ROUTER.md` to select
     the specific sub-lenses the diff can reach, then inject them:
     `launch-checker.sh --lens redteam/injection --lens qa/coverage -- -p "..."`. Do NOT dump a whole role;
     load only the sub-lenses that apply — focused context is the point.
   - **No harness? The cross-model check stays intact via a fallback.** If `launch-checker.sh` exits with
     `SM_NO_CHECKER_HARNESS` (or `/secondmate-doctor` reports no checker harness), spawn the checker as a Claude
     sub-agent via the Agent tool on a **different model than the maker**, tell it review-only (no edits, no
     state-changing commands), paste in `${CLAUDE_PLUGIN_ROOT}/bin/checker-prompt.md` +
     `${CLAUDE_PLUGIN_ROOT}/bin/checker-envelope.md` + the selected `bin/lenses/<role>/<sub>.md` files + the
     diff, and require it to end with the `{verdict}` envelope. It is weaker than a cross-vendor harness (same
     model family) but keeps maker ≠ checker and the deterministic verdict. Capture its final message and
     treat it exactly like harness output below.
   - Branch on the verdict deterministically, NOT on the checker's prose:
     `${CLAUDE_PLUGIN_ROOT}/bin/verdict.py <checker-output>` → exit 0 pass / 1 fail / 2 error|refused. When lenses were injected via `--lens`, add `--lenses <comma-separated-list>` to cross-check the envelope's `lens_coverage` field (the checker is told each lens's exact name and should report `{"lens_coverage": {"<name>": true, ...}}`). A missing lens triggers `ambiguous` (exit 2). Also for `fail` verdicts, findings must contain file:line tokens or the explicit escape hatch `[NOLOC]`; invalid findings trigger `ambiguous`.
   - **On `fail` — loop back to the maker, never fix inline as supervisor.** The supervisor reads the
     findings, synthesizes a concrete fix plan, then routes it to the task-scoped maker:
     - *Pi herdr maker (still running):* `herdr agent prompt sm-pi-<task-id> "<fix plan> Before replying DONE, write/update the round-state handoff file (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<fix plan>")" --wait --timeout 600000`
     - *Pi herdr maker (exited/done):* `herdr agent start sm-pi-<task-id> --kind pi --pane <root_pane_id> -- --provider amazon-bedrock --model qwen.qwen3-coder-next --thinking medium --extension "${CLAUDE_PLUGIN_ROOT}/bin/scope-guard-extension.ts"`, then prompt with the same fix plan and checklist.
     - *Headless pi maker:* `cd <wt> && run-round.sh --label sm-pi-<task-id> -- pi --provider amazon-bedrock --model qwen.qwen3-coder-next --thinking medium --extension "${CLAUDE_PLUGIN_ROOT}/bin/scope-guard-extension.ts" -p "<fix plan> Before replying DONE, write/update the round-state handoff file (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<fix plan>")"`
     - *Claude maker:* `herdr agent prompt sm-<task-id> "You are the maker. Do NOT invoke /loop-task or secondmate. <fix plan> Before replying DONE, write/update the round-state handoff file (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<fix plan>")" --wait --timeout 600000`
     The supervisor NEVER writes project code itself — synthesizing the fix plan is analysis, not implementation.
     Every fix round goes through Check with a refreshed `--live-text` and an incremented unique round marker.
     **Restart amplio-style hybrid:** When the supervisor restarts a maker mid-round (due to `loop-guard.sh` exit-5 restart signal,
     timeout, or idle-watchdog) rather than the maker reaching DONE naturally, the supervisor itself synthesizes the
     deterministic `Completed` and `Relevant Files` sections from `git log` and `git diff` in the worktree (and
     `bin/run-round.sh`'s own audit record if one exists for that round) before restarting the fresh maker.
     The maker owns only the four prose sections it has direct knowledge of: Objective, Active, Blocked, Next Move.
     This hybrid approach ensures deterministic history while giving the maker room to provide the 'why' when it
     gets the chance to write it after a restart.
   - **On `error` or `refused`** — do not retry via the maker. Inspect the checker output, fix the checker
     invocation (bad args, missing context) or escalate to the human. `refused` always escalates.
   - **Log the round.** After every checker verdict (pass, fail, error, or refused), append a metrics
     record: `${CLAUDE_PLUGIN_ROOT}/bin/log-round.sh --task <id> --round <N> --maker claude|pi --verdict <verdict> [--tag <finding-category>]... [--cost <n>] [--duration <n>]`.
     Supply one `--tag` per recurring finding category you'd tag it with in `audit/decision.md` anyway
     (e.g. `real-bug`, `scope-creep`, `fake-test`, `not-committed`) — this is structured data alongside the
     prose audit trail, not a replacement for it. `--cost`/`--duration` are optional, only if already at
     hand (e.g. from a herdr pane's own cost/elapsed display) — never scrape or parse for them.

5. **Gate** — before integrating anything:
   `${CLAUDE_PLUGIN_ROOT}/bin/verify-gate.sh --worktree <wt> --base <branch> --checked-sha <the exact sha the checker reviewed> [--test "<cmd>"]`
   — integrate only on `PASS`. Passing `--checked-sha` is mandatory: it catches a maker pushing commits after the checker approved.

6. **Hold** — every human-gate decision (merge / risky / outward-facing) is durable, not chat memory:
   `${CLAUDE_PLUGIN_ROOT}/bin/hold.py hold --task <id> --q "..." [--opts "a|b|c"]`, acted on only after
   `${CLAUDE_PLUGIN_ROOT}/bin/hold.py answer <id> --a "..."`. The plugin's SessionStart hook surfaces open holds
   each session, so a restart never drops a pending gate — reconcile any it reports before new work.

8. **Integrate** only after a passing verdict + a `PASS` gate + an answered hold. `scout` tasks stop at a report.

9. **Teardown** — immediately after integration, close everything created for this task:
   ```bash
   herdr pane close "$ck"                              # checker pane (if visible path was used) - close BEFORE workspace removal
   herdr worktree remove --workspace <workspace-id>   # removes git worktree + herdr workspace
   git branch -d sm/<task-id>                          # delete the merged branch
   ```
   A merged task that leaves a worktree or branch behind is incomplete. The worktree must not outlive its task.

   **IMPORTANT:** `caffeinate-guard.sh stop` is SESSION-SCOPED, not per-task. Call it ONCE yourself, directly,
   only after you have confirmed EVERY task/worktree in that batch has been torn down. Never call `stop` inside
   a task's per-task teardown — sibling tasks may still be running and need sleep prevention.

10. **Audit trail** — after teardown, append to `audit/flow.md` and `audit/decision.md` in the **primary checkout**:
   - `audit/flow.md` — which maker path was chosen and why, planner model list if committee ran, round count, outcome.
   - `audit/decision.md` — what the maker decided, what the checker found, every gate auto-approved or escalated and why.
   Append, never rewrite. Commit separately in the primary repo — they do not touch the worktree and cannot stale the checked SHA.
   Both files are `@`-imported in `CLAUDE.md` and auto-loaded into every session as context. Skip for trivial one-shot edits.

## Fan-out to concurrent sub-supervisors (hard-capped at 2)

Everything above is the default: one supervisor, one task, one loop at a time. This section is an
**opt-in** variant for the one specific case where a human hands you 2 genuinely independent tasks and
wants them run concurrently — it is never automatic, and the single-task loop above remains the default
for everything else, including most multi-task requests (queue them sequentially through the normal loop
unless the human explicitly wants concurrency).

**Mechanism.** Make ONE Agent-tool call carrying AT MOST 2 tool-use blocks — a **hard cap**, not a
tunable parameter. There is no N>2 variant of this pattern; if there are more than 2 independent tasks,
run 2 now and queue the rest for the next round. Each of the (at most 2) tool-use blocks launches a
**FRESH** sub-agent — never `fork`. `fork` inherits the dispatcher's own conversation context, which is
wrong here: each sub-supervisor must reach its own triage/routing/verdict judgment calls from a clean,
unpolluted context with its own autonomous Bash/herdr access, not one contaminated by a sibling task's
planning, findings, or in-flight state. **Name each Agent-tool call `sm-<task-id>`** — never leave `name`
unset, which defaults to a generic, indistinguishable label (e.g. just the subagent_type). This outer,
dispatcher-level name is distinct from step (b) below's inner maker naming (`sm-<task-id>`/`sm-pi-<task-id>`),
which is decided later once the sub-supervisor itself routes to a maker — use plain `sm-<task-id>` here
regardless of which maker that sub-supervisor ends up choosing internally. The name is also the handle used
to resume a sub-supervisor later via `SendMessage` (passing it as the `to` field) once its hold is answered.

**Each sub-supervisor's prompt must instruct it to, in this order:**

a. **Claim first, as its literal first action.** Run `bin/claim-ledger.py claim --task-id <task-id>
   --owner sm-<task-id>` before anything else. If the claim fails, abort immediately and emit
   `SM_REFUSED:claim-failed` as its final output — do not proceed, do not retry, do not fall back to
   `--steal`. `--steal` is a human-supervised override and stays exactly that under this pattern too: a
   sub-supervisor must never call it itself.

b. **Derive every downstream name deterministically from `<task-id>`, using this repo's own existing
   convention — never invent a new one:**
   - branch: `sm/<task-id>`
   - worktree label / agent name: `sm-<task-id>` (Claude maker) or `sm-pi-<task-id>` (pi maker)
   - pane: whatever `herdr worktree create` returns as `.result.root_pane.pane_id` — never independently
     named or guessed.

c. **Run the existing solo secondmate SOP completely untouched** — plan-committee, maker routing, checker
   rounds, verify-gate, exactly as described everywhere above. This pattern changes nothing about how a
   single task runs, only how it gets launched. A sub-supervisor is not a different kind of supervisor; it
   is this same SOP, running with its own claimed task-id.

d. **Open its own `bin/hold.py hold --task <task-id> --q "..." --sha <checked-sha>` entry for the merge
   decision once verify-gate has passed, and WAIT for a genuine human answer** — never assume, never
   auto-answer, matching the existing single-task loop's Gate → Hold → Integrate contract exactly (e.g.
   `bin/hold.py hold --task <task-id> --q "merge <task-id> (checker PASS, verify-gate PASS at
   <checked-sha>)?" --sha <checked-sha>` — `--task` and `--q` are required, `--sha` is optional but should
   always be supplied here so the hold is bound to the exact reviewed commit). The merge-or-not judgment
   call belongs to that sub-supervisor and the human who answers its hold — it must NEVER defer that
   decision to the top-level dispatcher, which has no visibility into that task's actual diff/findings.

e. **Only once that hold is answered, merge: call `bin/merge-sequencer.sh` itself** with its own claimed
   `--branch`/`--worktree`/`--checked-sha`. It may legitimately queue behind a sibling sub-supervisor's own
   concurrent merge attempt on the same `--repo` — that is the singleton lock working exactly as intended,
   not a bug to diagnose or work around.

f. **Release its claim on every terminal path** — success, refusal, or stuck — via `bin/claim-ledger.py
   release --task-id <task-id> --owner sm-<task-id> --token <token>`. A claim released only on the happy
   path leaks on every other path.

g. **Emit exactly ONE completion tag, on its own line, as its literal final output:**
   `SM_DONE_MERGED:<sha>`, `SM_STUCK_NEED_HUMAN:<reason>`, or `SM_REFUSED:<reason>`. Nothing after it,
   nothing instead of it.

**The dispatcher's (top-level supervisor's) own role is a hard boundary — state it to yourself explicitly
before fanning out:** you only launch the (at most 2) sub-agents and, once each finishes, parse its final
text with `bin/dispatch-report.py`. You NEVER re-read a sub-supervisor's prose to make your own judgment
call about what happened — the tag is the only signal you act on. On `SM_STUCK_NEED_HUMAN`, your only
allowed action is relaying that sub-supervisor's own reported reason to the real human, verbatim — never
attempting to resolve it yourself, even if you think you know how.

```
${CLAUDE_PLUGIN_ROOT}/bin/dispatch-report.py <sub-supervisor-final-output-file>
# exit 0 = SM_DONE_MERGED   -> integration for that task-id is done
# exit 1 = SM_REFUSED       -> relay the refusal reason to the human, task-id never started
# exit 2 = SM_STUCK_NEED_HUMAN -> relay the reported reason verbatim to the human; do not resolve it yourself
# exit 3 = no tag found at all -> treat as a parse failure and escalate; do not guess what happened
```

**Named limitation — no liveness/reaping, by design, matching `claim-ledger.py`'s own existing
disclosure style.** This pattern has NO liveness/reaping mechanism of any kind. If a sub-supervisor's
process dies mid-task (crash, killed pane, disconnected agent), its claim is **not** automatically
released and its worktree is **not** automatically cleaned up. A human must notice and manually run
`bin/claim-ledger.py release` (or, if truly abandoned, `--steal` with a reason) plus manual worktree
teardown. Closing this gap — a liveness/reaping diagnostic that can tell a genuinely dead sub-supervisor
apart from one that is merely slow — is a separate, deferred future task, not part of this one.

## Visible orchestration in herdr (when HERDR_ENV=1)

**Strict rule: whenever `HERDR_ENV=1`, the checker MUST run in a visible pane — headless checker execution is
prohibited in that case**, not just an optional alternative (see step 4's "Check" section above). The maker
runs directly on the worktree's root_pane the same way. You stay in your pane and drive the others via the
herdr CLI. First check: `${CLAUDE_PLUGIN_ROOT}/bin/herdr-pane.sh check` (if it fails — and only then — fall
back to the headless path). Every split uses `--no-focus` so the captain's focus never moves.

- **Maker pane** — start the Claude maker directly on the root_pane from `herdr worktree create` (no split needed since the root_pane's cwd is already the worktree), then drive via `agent prompt`:
  ```bash
  herdr agent start sm-<task-id> --kind claude --pane <root_pane_id> -- --permission-mode auto || { echo "herdr agent start failed — abort" >&2; exit 1; }
  herdr agent prompt sm-<task-id> "Implement: <goal>. You are the maker — write the code, run tests, commit to this worktree, then reply DONE.  *Before replying DONE, write/update the round-state handoff file* (`${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}`). Write it ATOMICALLY (write to a temp file in the same directory, then `mv` over the real path — never a direct partial write). Include the four prose sections you have direct knowledge of: Objective, Active, Blocked, Next Move. The supervisor will populate Completed and Relevant Files from git history when synthesizing a restart; you can leave placeholder text or omit them.Do NOT invoke /loop-task or secondmate; the supervisor owns the checker loop.

$([ -f "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}" ] && { echo '--- Previous round handoff ---'; cat "${SM_ROUND_STATE:-${SM_LOOP_STATE:-.secondmate}/round-state.md}"; })
$(${CLAUDE_PLUGIN_ROOT}/bin/lesson-lookup.py --task "<goal>")" --wait --timeout 600000
  ```
  If Claude shows a one-time folder-trust prompt, accept it once: `herdr agent send-keys sm-<task-id> enter`. The maker's output is
  its file edits — read them with `git -C <worktree> diff`, not from the pane.
- **Checker pane** — the edit-locked checker, run headless IN the pane so it's visible AND capturable.
  Start by splitting off the SAME dedicated workspace's root_pane (not off the supervisor's pane):
  ```bash
  ck=$(${CLAUDE_PLUGIN_ROOT}/bin/herdr-pane.sh split --pane <root_pane_id> --dir down)
  # Write the full checker invocation to a script file first — herdr pane run's argv-to-PTY-line
  # reconstruction does not preserve shell quoting for multi-token/multi-command strings
  cat > /tmp/checker-<task-id>-r<N>.sh << SCRIPT_EOF
${CLAUDE_PLUGIN_ROOT}/bin/launch-checker.sh \
  --lens qa/coverage --addendum-text '...' \
  --diff-base <base-ref> --repo <wt> \
  --live-text '<what changed this round>' \
  -- -p 'Review the change.' ; echo ___SM_R<N>_DONE_\$?
SCRIPT_EOF
  herdr pane run "$ck" bash /tmp/checker-<task-id>-r<N>.sh
  herdr pane wait-output "$ck" --regex "___SM_R<N>_DONE_[0-9]+" --timeout 600000
  herdr pane read "$ck" --source recent-unwrapped --lines 400 > /tmp/sm-checker.out
  ${CLAUDE_PLUGIN_ROOT}/bin/verdict.py --lenses qa/coverage /tmp/sm-checker.out
  ```
- **Watch + integrate from your pane** — `herdr agent get/read sm-<task-id>`, `herdr pane read "$ck"`; then the
  usual verify-gate + hold. You can't answer another pane's live prompt, so run any gated command yourself
  in the supervisor context (still a separate context, so maker ≠ checker holds).
- **Clean up ONLY the panes you created**: `herdr pane close "$ck"` (no `$mk_pane` to close since the maker ran on the root_pane directly).

  **IMPORTANT:** `caffeinate-guard.sh stop` is SESSION-SCOPED, not per-task. Call it ONCE yourself, directly,
  only after you have confirmed EVERY task/worktree in that batch has been torn down. Never call `stop` inside
  a task's per-task teardown — sibling tasks may still be running and need sleep prevention.

Not in herdr (`HERDR_ENV != 1`)? Use the headless path — in-process maker sub-agent + `run-round.sh`-wrapped
checker. Same loop, same guards, just not visible.

## Not for
Trivial one-shot edits, read-only questions, or work with no verifiable result — do those directly.