---
name: scorpio-loop
description: >
  Autonomously work through an issue backlog by looping a per-issue ship pipeline.
  Use when the user wants to ship multiple issues in one go — "ship the backlog",
  "work through the backlog", "ship the next N issues", "run the loop", "keep
  shipping until you hit a problem". This is the sequential control loop that sits
  ABOVE the per-issue engine: it decides WHAT to ship next (prioritization +
  dependency/scope judgment), then drives each issue to Done via the ship skill,
  one at a time, until the backlog is drained or a failure stops it. Portable
  across repos — all project specifics are resolved from config/detection, not
  hardcoded.
argument-hint: "[count | all] [--unattended] [tracker filter]"
---

# scorpio-loop — sequential backlog control loop

Drive an issue backlog from **scoped → Done**, one issue at a time, by looping a per-issue ship pipeline. **You (the main session) are the control loop AND the coach** — you pick the next issue and dispatch the per-issue engine; you do not write code or run QA yourself. Sequential by design: each issue is fully merged into the base branch before the next is picked, so dependencies resolve naturally and there are no cross-issue merge conflicts to plan around.

## Why sequential (not a parallel swarm)
Shipping one issue at a time dissolves three whole problem classes: no git-worktree isolation, no file-collision pre-planning, and no dependency topo-sort — because issue *B* always branches off a base that already contains the merged *A*. The cost is wall-clock, but the heavy work (the per-issue pipeline) dominates anyway, and parallelism is preserved where it's safe: the per-issue engine still fans out its QA reviewers concurrently *within* each issue — **where the harness supports it** (the `parallelFanout` capability). On a harness without it (e.g. Codex's conservative manifest), reviewers run sequentially; correctness is unchanged, only wall-clock grows.

**Agent depth:** main = control loop (0) → ship engine (1) → QA reviewers (2). On Claude Code this is shallow, well under the depth-5 background cap. On a harness whose manifest reports `spawnFreshReviewer.available: false`, the engine runs each reviewer in a separate clean session per the manifest's degrade path (see [docs/CAPABILITIES.md](../../docs/CAPABILITIES.md)) — the fresh-context guarantee is preserved even where the depth-2 subagent mechanism isn't. You stay reachable to stop/inspect between issues.

## Resolve configuration (do this first, then echo it back)
Detect these from the repo and **print the resolved values to the user before starting** so they can correct anything:

| Knob | How to resolve | Fallback |
|------|----------------|----------|
| **TRACKER** | `tracker.adapter` from config; else detect (`$LINEAR_API_KEY` + `scripts/tracker/linear.mjs` → linear; else `gh` available → github). All tracker calls go through one entrypoint — `node scripts/tracker.mjs <verb>` — which loads the API token from `tracker.credentialEnv` (never `~/.bashrc`). See [docs/TRACKER-INTERFACE.md](../../docs/TRACKER-INTERFACE.md). | ask the user |
| **ISSUE_ID pattern** | `tracker.issueIdPattern` from config (e.g. `PROJ-\d+` on Linear, `#\d+` on GitHub). | infer from a sample issue |
| **BASE_BRANCH** | `git.baseBranch` from config; else `git symbolic-ref refs/remotes/origin/HEAD`. **Never a protected branch** (`git.protectedBranches`) unless the user confirms. | ask the user |
| **SHIP_SKILL** | the per-issue engine skill. | `scorpio-ship` |
| **GATES** | `gates.commands` from config (else detected from `package.json` scripts), run through `node scripts/gate.mjs <name>` / `gate.mjs required` (see [docs/GATES.md](../../docs/GATES.md)). | whatever the SHIP_SKILL defines |
| **PUSH** | `git.pushAfterMerge` from config. | per user decision (default: yes) |
| **MODELS** | `models.<role>` from config — the per-role model (+ optional `effort`) each phase targets (plan / implement / review / synthesize / default). The engine resolves and applies them per issue; echo the resolved per-role models so the operator sees which model each phase targets. **Honored to the harness's granularity** (`modelSelection` capability) — advisory on Claude Code. See [docs/CONFIG.md](../../docs/CONFIG.md#per-role-models). | unset → harness main-session model |
| **SELECT policy** | see "Pick the next issue" below. | — |

If a knob can't be resolved, **stop and ask** — don't guess the tracker or the base branch.

## Preflight (once, before the loop)
1. Working tree is clean; you're on / can branch from BASE_BRANCH.
2. **Establish the gate baseline** (`node scripts/gate.mjs required` on BASE_BRANCH HEAD). BASE_BRANCH does **not** have to be pristine: record any **already-red** required gates as the inherited baseline (pre-existing flaky/env-specific failures the loop must not chase). What poisons the loop is *new* redness, not pre-existing redness — the engine judges each issue against the baseline it captures at its own start (SHIP_SKILL Step 2), so a dirty-but-stable base is fine to run on. Only stop here if the base is red in a way you can't explain (e.g. mid-rebase, build totally broken). See [docs/GATES.md](../../docs/GATES.md) §Differential gating.
3. Tracker is reachable (list a few issues).
4. Echo the resolved config **and the planned issue order** (the queue you intend to ship), then **get one go-ahead** unless `--unattended` was passed. This single up-front confirmation covers the autonomy + token cost of looping the engine (each iteration spawns QA sub-agents). After go-ahead, run without per-issue prompts.

## The loop
Repeat until the queue is empty, the requested `count` is reached, or a failure stops it:

**1. Pick the next issue (coach judgment).** From open issues ordered by priority, select the highest-priority one that is **shippable now**. Skip / exclude:
   - issues with **unshipped dependencies** (sequence them later);
   - **non-code / ops-only** issues (manual admin, content, business tasks — flag them as off-cycle, don't try to "ship" them);
   - issues already **`FAILED`-prefixed** (a prior stop you haven't triaged);
   - issues with no clear plan/acceptance criteria — either let the engine's scope gate handle it, or skip and note it.
   If nothing is shippable → the loop is done; report and exit.

**2. Announce.** State the picked issue, why it's next, and the one-line plan. (No prompt — you already have go-ahead.)

**3. Ship it.** Move the issue to In Progress, then invoke **SHIP_SKILL** for this single issue. The engine owns implementation, gates, the **fresh-context QA fan-out**, the merge into BASE_BRANCH, the post-merge differential gate, the push, and moving the issue to **Done**. Do not duplicate its work — orchestrate, collect its verdict.

**4. On success.** The engine has already merged + pushed + closed the issue. **Between-issue gate (differential):** re-run `node scripts/gate.mjs required` on BASE_BRANCH HEAD and compare to the baseline — cheap insurance against a regression the per-issue gate missed. A **newly** red gate (green at the last baseline, now red) is a real regression → treat as a FAILED stop (guardrail below). A gate that was **already** red in the baseline staying red is **not** a regression — carry it forward as the new baseline and keep going. Record the result in the run ledger. Loop.

**5. On FAILED.** A failure = a blocking QA/security finding that survives rework, an unresolvable merge conflict, or a red post-merge gate (engine leaves BASE_BRANCH clean in all cases). Apply the **retry-once-then-stop** policy:
   - **Retry once:** re-dispatch SHIP_SKILL for the same issue, fresh.
   - **Still failing → STOP the entire loop.** Prepend `FAILED ` to the issue's tracker title, post a prominent comment with the blocker + file:line evidence + which step failed, and surface it to the user. Do **not** move on to other issues — a hard stop is the chosen policy so problems get human eyes before the backlog drains past them.

## Guardrails — stop and surface when
- The tracker, base branch, or another config knob can't be confidently resolved.
- A pick would require touching a protected branch (`git.protectedBranches`), committing a secret, or a token-heavy Workflow not yet approved.
- A merge conflict needs judgment the engine couldn't cleanly resolve (never auto-resolve conflicts blind).
- The between-issue gate shows a **newly** red gate (regression vs the baseline) you can't trace to the issue just merged. (A pre-existing baseline failure staying red is expected — carry it forward, don't stop.)
- `count` reached, queue empty, or the first FAILED-after-retry — these are normal stops; report and exit cleanly.

## Run ledger / output
Keep a running ledger and emit a final report:
- **Per issue:** id · title · branch · merge SHA · gates (✅/❌) · QA/security verdict · tracker state.
- **Summary:** issues shipped, issue (if any) that stopped the loop and why, anything skipped (deps / ops-only / unscoped) with the reason, follow-up issues created by the engine, and the final state of BASE_BRANCH (green + pushed).

## What this skill is NOT
- **Not the per-issue engine.** It never writes code, generates tasks, or runs QA — that's SHIP_SKILL. It selects, dispatches, gates between issues, and enforces the failure policy.
- **Not a parallel orchestrator.** One issue at a time, on purpose. If the user explicitly wants a parallel swarm, that's a different design (worktree isolation + collision planning + serial gated merge) — surface the trade-off rather than silently fanning out.
- **Not a planner for vague work.** If an issue lacks a plan, let the engine's scope gate handle it or skip it — don't invent scope.
