---
name: scorpio-ship
description: Drive a single tracker issue from scoped to shipped using the Scorpio skill pipeline run inside Claude Code, with the issue tracker as the spine and quality-gates as the definition of done. Use when the user wants to build/deliver a specific issue end-to-end ("ship this issue", "run the pipeline on PROJ-7 / #42", "build this issue").
argument-hint: "[issue id]"
---

# scorpio-ship

Take one tracker issue from **scoped → shipped**, orchestrating Scorpio's SDLC skills as a repeatable pipeline. The agent (you) is the orchestrator; sub-agents and Workflow are used only where they earn it.

## Operating rule — run the skills in Claude Code, never the external CLI
**Do NOT invoke the external Scorpio CLI orchestrator (`scorpio …`) to run these skills.** Run the Scorpio *skills* directly here in Claude Code, where you have full read/write to the repo and git. This is a deliberate policy — direct edits, commits, and orchestration control all live here. If you feel the urge to shell out to the Scorpio CLI, stop and do it in-thread or via the Agent/Workflow tools instead.

## Architecture
- **The issue tracker = the spine** — canonical status & priority; issue IDs match `{tracker.issueIdPattern}` (e.g. `PROJ-12` on Linear, `#42` on GitHub). The issue *description* holds the plan.
- **`{docs.planning}` + `{docs.tasks}` = the detail** — specs, task files, QA reports; referenced from the issue.
- **Quality gates = definition of done.** Gates are config-resolved shell commands run through one entrypoint — `node scripts/gate.mjs <name>` (see [docs/GATES.md](../../docs/GATES.md)). Honor `gates.autoGate`: under `posttooluse` a harness edit hook auto-runs `check` for you; under `explicit` (the default, and required on harnesses without an edit hook) **you run `gate.mjs check` yourself** after edits.
- **Match orchestration weight to task shape:** sequential / shared-type churn → main thread, one focused pass. Wide & independent → parallel sub-agents (design/advisors) or a Workflow fan-out for execution. **A token-heavy Workflow is opt-in — confirm with the user before launching one.**

## Preconditions
- **Tracker access:** Scorpio talks to the tracker through **one entrypoint** — `node scripts/tracker.mjs <verb>` (see [docs/TRACKER-INTERFACE.md](../../docs/TRACKER-INTERFACE.md)). The dispatcher resolves the adapter (Linear / GitHub) and reads the API token from the env var named by `tracker.credentialEnv` in config. **Never** source a key from `~/.bashrc`. If the token isn't set, the dispatcher errors clearly — ask the user to export it; don't guess or hardcode a key.
- **Never touch a protected branch.** Branch off the resolved `git.baseBranch` (detected via `origin/HEAD` if config omits it) and merge back into it; never commit to or merge into anything in `git.protectedBranches` (default `[main]`). Each workstream gets its own feature branch (optionally prefixed by `git.branchPrefix`).
- **Harness capabilities.** This pipeline needs to spawn **fresh-context** QA reviewers (and ideally fan them out in parallel). Resolve the running agent's manifest (`agents-manifest/<agent>.<surface>.json`; see [docs/CAPABILITIES.md](../../docs/CAPABILITIES.md)). On Claude Code these are native (Agent tool); where a capability is absent (e.g. Codex's conservative profile), **follow the manifest's `degrade` path and surface it** — never silently drop the fresh-context guarantee.
- **Per-role model selection.** If `scorpio.config.json` defines `models`, each pipeline phase has a target model + optional `effort`: **plan** (steps 1, 3) → `{models.plan}`, **implement** (step 4) → `{models.implement}`, **review** (step 6 QA fan-out) → `{models.review}`, **synthesize** (advisor/objective synthesis, when run) → `{models.synthesize}`; an unset role falls back to `{models.default}` → the harness's main-session model. This is a **declaration honored to the granularity the harness supports** (the `modelSelection` capability). On Claude Code the Agent tool can't pick a model per dispatch from config, so it's **advisory**: when you spawn a phase's sub-work, **state the intended per-role model** in the dispatch so the operator can apply it via the role agent's frontmatter `model:` or a global `CLAUDE_CODE_SUBAGENT_MODEL` for the run (the Claude Agent SDK honors it per agent-definition). Choosing a model **never** changes the fresh-context QA guarantee — only which model runs the reviewer. If `models` is unset, every phase runs on the main-session model (no behavior change). See [docs/CONFIG.md](../../docs/CONFIG.md#per-role-models).

## Tracker cheatsheet (one entrypoint, no key sourcing — `<id>` matches `{tracker.issueIdPattern}`)
- `node scripts/tracker.mjs view <id>` — full issue (description = plan), state, project, labels
- `node scripts/tracker.mjs update <id> --state "In Progress"` — move state
- `node scripts/tracker.mjs comment <id> --body "..."` — add a comment
- `node scripts/tracker.mjs list --state "In Progress"` — board view

## Pipeline

**0. Load context.** `view` the issue (its description is the plan), note its project/epic, priority, labels, and dependencies/siblings. If it depends on unshipped issues, **stop and flag**.

**1. Scope gate.** If the description lacks a clear plan + acceptance criteria, scope it first: `scorpio-feature-plan` (or `scorpio-feature-intake` if requirements are genuinely unclear — that needs the user). Write the resulting plan back into the issue description, then continue. **Never build a vague issue.**

**2. Start.** Move the issue → **In Progress**. Ensure you're on a feature branch off `git.baseBranch` (never a protected branch).
   - **Fresh repo (zero commits)?** Don't improvise the issue's baseline/scaffolding commit straight onto a protected branch (or even onto base as raw work). Bootstrap per `scorpio-commit` §"Fresh repo": seed `git.baseBranch` with an empty root commit, then branch the feature off it — the deliverable still arrives by merge, like every other issue.
   - **Capture the gate baseline first.** Before branching, on `git.baseBranch` HEAD run `node scripts/gate.mjs required` and record which required gates are **already red** — the *inherited-red set* for this issue. A baseBranch can carry pre-existing failures (flaky/env-specific tests, a local env that differs from CI — e.g. tests green in CI's clean env but red locally); those are **not yours to fix** and must not block your issue. The post-merge gate (Step 8) judges *new* redness against this set, not absolute green. (Per-issue capture means the baseline is always current — nothing to maintain, nothing to rot.) See [docs/GATES.md](../../docs/GATES.md) §Differential gating.

**3. Generate tasks.** For anything non-trivial, run `scorpio-task-plan` from the plan → task files in `{docs.tasks}/` with acceptance criteria and stable IDs. Skip for a single atomic change that already has clear criteria.

**4. Implement.** `scorpio-execute` / `scorpio-edit` (the **implement** role → `{models.implement}`), weighted to shape:
   - *Small / sequential / shared-types* → main thread, focused edits.
   - *Wide / independent* → fan out `Explore`/sub-agents for design; for parallel edits use a **Workflow** with `isolation: 'worktree'` (opt-in — ask first).

**5. Quality gates (DoD).** Run the config-resolved gates via the runner: `node scripts/gate.mjs check` (lint + typecheck), `node scripts/gate.mjs unit`; add `node scripts/gate.mjs build` / `e2e`, or DB/migration gates, when relevant. `node scripts/gate.mjs required` runs the full definition-of-done set. Under `gates.autoGate: "explicit"` you must run `check` yourself after edits; under `"posttooluse"` the harness hook does it — either way, fix anything a gate flags before proceeding. **Discount the Step-2 inherited-red set:** a failure that was already red on the baseline (pre-existing flaky/env-specific breakage) is not yours to fix — converge the gates *your change* affects, don't chase baseline noise.
   - *Expected noise:* during a shared-type refactor (e.g. renaming a field consumed across files), the `check` gate will fail on the **intermediate** edits until every consumer is updated. That's expected signal, not a regression — edit in dependency order and keep going until the final state is green.

**6. Review (adversarial) — code QA AND browser QA must BOTH pass to close.**
   - **Run every QA pass in a fresh-context reviewer (the `spawnFreshReviewer` capability — the Agent tool on Claude Code), NOT in the main/implementation thread.** These reviewers are the **review** role → run them on `{models.review}` (state the intended model in each dispatch; see Per-role model selection). The model choice is orthogonal to freshness — a stronger review model is an honest check, not a substitute for fresh context. A reviewer that shares the session that wrote the code inherits its assumptions and blind spots and will rubber-stamp. Spawn each reviewer with empty context so it judges the diff against the spec objectively, without trusting that the implementer's code or claimed outputs are correct. This applies to `scorpio-qa`, `scorpio-qa-final`, `security-reviewer`, `migration-reviewer`, and browser/visual QA. The main thread only orchestrates (dispatches reviewers, collects verdicts, loops rework) — it does not self-certify its own work.
     - **If the harness can't spawn a fresh reviewer** (manifest `spawnFreshReviewer.available: false`, e.g. Codex's conservative profile): do **not** review in-thread. Run each reviewer in a **separate, clean session** and **emit the degrade warning to the user** ("a reviewer sharing the implementer's session rubber-stamps — review in a clean session"). If `parallelFanout` is unavailable, run the reviewers **sequentially** and note that wall-clock grows — correctness is unchanged.
   - **Calibrate the reviewers: be critical, but a clean pass is a valid outcome.** Tell each reviewer explicitly that "no bugs found and the output meets the spec" is a fully acceptable verdict — do NOT invent, pad, or escalate marginal findings to look productive. The goal is an honest objective check, not a quota of problems. Demand file:line evidence and a concrete fix for every real finding; everything else is a clean pass.
   - **Reviewers see the working tree, not a branch diff.** Pre-commit, `git diff <base>...<branch>` is empty — tell every reviewer subagent to review the **staged/working-tree diff** (`git diff` / `git diff --staged`), or commit first then review.
   - **Inject the stack profile.** The `security-reviewer` and `migration-reviewer` are stack-neutral — pass the resolved `stackProfile` (from `scorpio.config.json`) into each reviewer's dispatch prompt so it reviews against this project's stack (auth, payments, orm/db, storage…). If no `stackProfile` is configured, the reviewers fall back to a language-agnostic review.
   - `scorpio-qa` (fresh-context reviewer) against the acceptance criteria.
   - **Mandatory `security-reviewer` fresh-context reviewer** when the change touches the security-sensitive surfaces named in `stackProfile` — auth / payments / secrets / sessions / object-storage (`stackProfile.auth` / `payments` / `storage`).
   - **`migration-reviewer` fresh-context reviewer** when it touches schema, migrations, or db scripts (per `stackProfile.orm` / `db`).
   - **Browser/visual QA — applies only when the project has a browser UI** (`stackProfile.appShape` is a web shape, e.g. `mobile-first-web` / `desktop-web`). For `appShape: none` / `cli`, **skip 6a/6b cleanly** — there is no UI to exercise, so the gate set (5) plus the code reviews above are the full DoD. When there *is* a UI, browser/visual QA is **MANDATORY for any UI-affecting change**, run via the project's `gates.e2e` command (an auth-bypass env like `VITE_E2E_AUTH_BYPASS=true` lets specs reach auth-gated pages where the stack provides one). It has **two distinct sub-steps and BOTH are required — functional assertions alone are NOT sufficient:**

     **6a. Functional QA (does it behave correctly).**
     - Update/extend the relevant e2e spec and run it via `node scripts/gate.mjs e2e` (or the spec-scoped command the stack uses, e.g. `npx playwright test <spec> --project=chromium`). A change that alters visible copy/prices WILL break stale specs — fixing them is part of the task.
     - Before scaffolding a new spec, **check for an existing spec that already mocks the relevant APIs** (e.g. `ui-redesign.spec.ts` mocks the listener routes) and extend it — the mock response shapes also double as a quick contract reference for the client.
     - Assert behavior, not just presence: filtering re-queries, navigation lands on the right route, disabled/empty/error paths fire.

     **6b. Visual QA (does it actually look right) — DO NOT SKIP. Passing functional assertions do not prove the UI looks correct.**
     - Capture screenshots and **actually view them with the Read tool** — a screenshot you don't open is not visual QA. Describe what you see and judge it against the design system.
     - Cover the **states and viewports that matter**, not just the happy-path desktop view:
       - **Viewports:** the ones that matter for the project's `stackProfile.appShape` — for a mobile-first web app, desktop AND mobile (capture the mobile width, e.g. `390px`; note that `fullPage` screenshots render `position:fixed` bars at viewport-relative offsets, which can look mid-page — a capture artifact, not a defect).
       - **States:** default/populated, filtered/active-selection, **empty**, **loading**, and **error**. Empty/zero-width containers are a common silent breakage (e.g. an `aspect-square` `CoverArt` with no width parent collapses to nothing).
     - Practical capture pattern: add a temporary `test(...)` to the spec that drives the screen into each state and calls `page.screenshot({ path: 'tmp/<state>.png', fullPage: true })`, run it, Read the images, then **remove the temporary test** before committing (keep only the durable functional assertions).
     - Fix visual defects you find (or, if out of scope, file a tracker follow-up) — and re-capture to confirm the fix.
     - **Cite the exact route(s)/URL(s) exercised** (e.g. `/browse`) and the states/viewports reviewed in the QA summary so the human can repeat the check. To reach a gated, API-backed page manually, run the project's dev server and **sign in for real**. Do NOT use an e2e auth-bypass flag (e.g. `VITE_E2E_AUTH_BYPASS`) for manual checks: such flags typically blank the real login UI while the server APIs still return 401 — they are **e2e-spec-only** (specs mock the API responses).
   - A task is **not closeable** until code QA (gates + reviews) AND functional browser QA (6a) AND visual QA (6b) are all green. Loop `scorpio-qa-rework` until no blocking findings, or escalate (standalone: to the user; under the control loop: return a **FAILED** verdict to the master — see Failure handling).

**7. Commit.** Conventional message; feature branch; **never** commit secrets/`.env`. End the message with the configured `commit.trailer` (from `scorpio.config.json`), filling its `{agent}` placeholder with the active coding agent's identity — e.g. with the default trailer and Claude Opus 4.8 as the agent:
   `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>`
   If `commit.trailer` is an empty string, omit the trailer. (See [docs/CONFIG.md](../../docs/CONFIG.md).)

**8. Merge & finish the git ops.** Once your change's gates are green (no *new* failures vs the Step-2 baseline), the pipeline finishes the work itself — there is **no human spot-check gate**; the adversarial QA *is* the definition of done.
   - Merge the feature branch → `git.baseBranch` (**never** a protected branch).
   - **Post-merge green gate (differential):** on `git.baseBranch` HEAD, re-run `node scripts/gate.mjs required` (plus any relevant `e2e`). This catches *integration* regressions the isolated feature branch couldn't see. Judge the result **against the Step-2 inherited-red set, not absolute green**:
     - A gate that was **green at baseline but is now red** = a regression **your** change introduced → **the merge is bad**: `git reset --hard` `git.baseBranch` to the pre-merge SHA, do NOT push, treat the issue as **FAILED** (see Failure handling). A *newly* red base is fatal in a sequential loop because the next issue branches off it.
     - A gate that was **already red at baseline and is still red** (same pre-existing failure) is **not your regression** → do **not** block, do **not** reset. Note it in the close-out, and **file a follow-up tracker issue once** for the pre-existing breakage (don't refile if an open one already exists) so it gets fixed without halting the loop.
   - Only when the differential gate passes (no new redness) **and `git.pushAfterMerge` is true**: **push `git.baseBranch`** to the remote.

**9. Close out in the tracker → Done.** Move the issue straight to **Done** (no longer In Review). Comment with: what shipped, commit SHA(s) + merge SHA, QA/security verdict, **browser-QA route(s)/URL(s) + states/viewports reviewed + how to reach them**, task-file links, and any deferred follow-ups — **create new tracker issues for follow-ups** rather than letting them vanish.

**10. Capture learnings.** Append any pipeline friction or improvement to `.scorpio/lessons-learned.md`, and note candidate edits to *this skill* for the retro step.

## Failure handling
A **failure** = a blocking QA/security finding that survives rework, a merge conflict you can't cleanly resolve, or a **newly**-red post-merge gate (a gate that was green at the Step-2 baseline and your change turned red — a *pre-existing* baseline failure that stays red is not a failure). In every case, leave `git.baseBranch` clean (reset any bad merge) and do NOT mark the issue Done.
- **Standalone run:** stop and surface the blocker (with file:line evidence) to the user.
- **Under the control loop:** return a **FAILED** verdict + blocker to the control master rather than prompting the user. The master owns the retry-once-then-stop policy and the `FAILED ` tracker title prefix; this skill's job is to merge+push+Done on success, or report the blocker on failure.

## Guardrails — stop (standalone: ask the user · under the loop: return FAILED) when
- Acceptance criteria are ambiguous, or dependencies are unshipped.
- QA/security finds a blocking issue that survives rework (see Failure handling).
- A step would touch a protected branch (`git.protectedBranches`), commit a secret, or require a token-heavy Workflow not yet approved. (Pushing `git.baseBranch` is part of the normal finish when `git.pushAfterMerge` is true; pushing/merging a protected branch still needs explicit go.)

## Output
A concise report: **issue · branch · commit SHA · gates (✅/❌) · QA/security verdict · functional + visual browser-QA route(s)/URL(s), states, and viewports exercised · tracker state · follow-ups created.** Always cite the route(s)/URL(s) the browser QA hit (and how to reach them locally) so the human can repeat the check, and confirm the visual states/viewports were actually viewed (not just asserted).
