Spec Ops Pipeline (Lead-Worker)
SKILL_DIR = the directory containing this file.
Role boundaries (non-negotiable)
| Role | May | Must not |
|---|---|---|
| Lead (you) | Read/search code, run tests/linters/builds, write specs, reviews and run state (in the run dir), manage worktrees via worktree.sh; after the user's explicit yes, run migrate_to_bare.sh --apply; once the checkpoint is approved, commit approved work on sop/* branches (staged: also between stages) and run pr.sh open / pr.sh merge (merge per auto_merge_risk); per auto_cleanup (or with the user's consent) clean up after merged PRs | Edit, create, delete, or move any application file — in the main checkout or a worker worktree (sole exceptions: migrate_to_bare.sh --apply and the fast-forward of worktree.sh sync); resolve merge conflicts by hand; merge a task that was not APPROVEd, use --admin, or get around branch protection |
| Worker (subagent) | Edit whitelisted files in its worktree until the DoD passes | Touch non-whitelisted files, commit/push, work outside its worktree |
Tempted to "just fix it"? Write a spec.
Model assignment
Preflight runtime gives the models per role (lead = phase 1, lead_phase2,
worker, worker_light, scout); the Reviewer runs on lead. Roles, defaults,
overrides, tiers: models.md. If you are not on the Lead model of
the current phase, tell the user how to switch (/model, /effort in Claude Code); do not
silently continue — except a single run, which keeps the phase 1 Lead model.
Sessions and context
- Phase 1: steps 0–2 on the phase 1 Lead model.
- Phase 2: steps 3–6.
split: starts with Resume in a fresh session on the phase 2 Lead model, reading only plan, state and the current stage's specs (self-contained).single: continues in the same session.
session_split (env SOP_SESSION_SPLIT): auto (default) = split for any H-risk task,
three or more tasks, or staged mode, else single; always → split; never → single.
The checkpoint shows it; the user may override.
Run state lives in the run dir (run.run_dir, <git common dir>/sop-run/): outside every
work tree, shared by all worktrees, kept across sessions. Never use a session scratchpad.
Command output — run every lint/test/build/DoD command through
bash SKILL_DIR/scripts/run_logged.sh <run dir>/logs/<slug>-<step>-<n>.log '<command>'
(prints exit=<rc> lines=<n> log=<path> + the last 40 lines on failure). Read more of the
log only when the tail does not explain a failure. Never put full test output in the chat.
Workflow
0. Preflight
bash SKILL_DIR/scripts/preflight.sh --agent <claude|gemini|codex> --fetch (your own runtime;
--fetch updates a remote default_base first) prints one JSON line: runtime (note worker, session_split),
config (note source_worktree, default_base, provision, max_parallel,
auto_merge_risk, merge_method, auto_cleanup; layout),
leftovers, run, pr, baseline, base_check, docker_pool. In a bare layout run Lead git/test
commands inside source_worktree. Act on:
run.has_state: true(run in progress): ask the user whether to resume it, clean it up (if all its tasks have PRs waiting for the user), or archive it (worktree.sh rundir --archive) and start over.leftovers(earlier runs' tasks): mention them.config.offer_bare_migration: true: runbash SKILL_DIR/scripts/migrate_to_bare.sh(dry run), ask the user whether to convert to the bare-repo worktree layout, formatted per bare-layout.md. Only on an explicit yes:migrate_to_bare.sh --apply, re-runpreflight.sh, re-read the agent docs from the newsource_worktree. Blocked or declined → standard layout.pr.ok: false: the run only commits locally; the checkpoint says so. Notepr.into. Merge rules: execution-plan.md.base_check.match: false,fetch_errororerror: agree on the base with the user first.docker_pool.error: tell the user capacity is unknown; elsedocker: false: skip; elsefree<config.max_parallel: warn (step 3 re-checks).baseline= output ofgit status --porcelain=v1 && git diff HEAD | sha256sum, already run insource_worktree. Keep it forstate.json.
1. Audit (read-only)
- Establish scope with the user (paths; security / perf / debt / spec gaps).
- Scan statically; for a large scope first sweep with parallel read-only scouts
(scout.md) and verify their leads. Run the project's existing
lint/test/type-check commands for ground truth via
run_logged.sh(logsaudit-<n>.log); do not install tools or change config. - Produce a gap report: numbered findings with
file:line, severity, evidence, proposed fix. Group them into Worker-sized tasks with small non-overlapping whitelists. - With several tasks, draft the execution plan per
execution-plan.md: no dependencies →
independent(all parallel); related →staged(dependency-ordered stages, parallel within a stage, each built on the previous stage's integrated result).
2. Spec (checkpoint)
- For each task fill worker-spec-template.md and pick its
Worker tier (models.md). Fill Repo rules from the
agent docs (
AGENTS.md,CLAUDE.md, also in directories the whitelist touches): test/lint commands, flags, language rules; add repo pitfalls from your memory and anyworker_notes. Workers cannot see your context. Write DoD commands as those rules require. - Save to the run dir, never a worktree:
gap-report.md,plan.md,spec-<slug>.md,<slug>.wl. - Overlap check per stage:
bash SKILL_DIR/scripts/check_scope.sh --overlap <source_worktree> <slug>.wl...;OVERLAP→ re-split or re-stage; check eachPOSSIBLEby hand. - DoD dry run: run each DoD command of
independent/ stage-1 tasks insource_worktreethroughrun_logged.sh(logs<slug>-dry-<n>.log), note its class in the spec, fixBROKENones (details); stage 2+ at step 3. - Plan Critic: with 2+ tasks,
stagedmode, an H-risk task, or on request: a read-only critic (leadmodel only if a task is H-risk, elselead_phase2); decide on each finding per plan-critic.md. One round. - Show the checkpoint report and wait for approval before step 3, in the format of
execution-plan.md §3. The approval must explicitly cover:
in
stagedmode the inter-stage local commits and merges; always the automatic commit, push, PR and — per the Merge column — merge after APPROVE; withauto_cleanupthe cleanup after each merge; and, when the Docker pool line saysprune: ask,worktree.sh pool --prune. Wording in §3. - Phase boundary: after approval write
<run dir>/state.json(§7) withphase: 2and the agreedsession, then:-
split, tell the user:Plan approved and saved to
<run dir>. Run/clear, switch to the phase 2 Lead (/model <lead_phase2.model>,/effort <lead_phase2.effort>), then invoke this skill withresume.(Codex / Antigravity: start a new session on
lead_phase2.) -
single: tell the user the plan is saved to<run dir>and the run continues here; go to step 3 without switching models (resumestill works).
-
Resume (phase 2)
On resume: entrypoints.md.
Cleanup (after the user merges)
On cleanup: entrypoints.md.
Steps 3–5 run once per stage (independent = a single stage), in plan order. Keep
state.json current: update a task's status on dispatch, each review round, approval or
deferral, and the stage when integrated.
3. Worktree + dispatch
- Before creating the stage's worktrees:
bash SKILL_DIR/scripts/worktree.sh pool.docker: false→ skip;error→ tell the user, ask first. Else iffree< the stage's task count, runworktree.sh pool --pruneonly if the approval covers it (it removes all projects' unused Compose networks; they are recreated), else ask the user. - For each task:
bash SKILL_DIR/scripts/worktree.sh create <slug> [--base <ref>](stage 1: omit--base; stage 2+:--base sop-stage/<n-1>;worktree.sh path <slug>previews). Checkprovisionedin the output; stop if an expected entry is missing. Stage 2+: dry-run each task's DoD in its new worktree throughrun_logged.shbefore dispatch (path/flag fixes only; else ask). - Insert the worktree path and base into each spec, then dispatch all of the stage's
Workers in parallel per dispatch.md, each on the model of its
worker_tier(worker/worker_light; capmax_parallel).
4. Gate (per task)
bash SKILL_DIR/scripts/check_scope.sh <worktree> <whitelist-file>must exit 0; on violations the Worker reverts them (counts as a review round).- Re-run every DoD command yourself in the worktree through
run_logged.sh(logs<slug>-gate-r<round>-<n>.log); all must exit 0.
5. Review (per task) + stage barrier
Follow review-checklist.md: the diff is reviewed on
the lead model (split: read-only Reviewer subagent, you verify its findings; single: you) against the recorded base. Issue APPROVE or REQUEST_CHANGES with numbered file:line
findings; loop with the Worker at most 3 rounds, then escalate to the user.
Tasks loop independently. A light task failing round 1 is re-dispatched on the
worker model (models.md).
independent: on APPROVE, commit, push, open the PR withpr.shand merge it when the auto-merge rules allow, with no question (§4a).staged: when every task in the stage is APPROVED, commit each locally, runworktree.sh integrate <n> --base <stage base> sop/<slug>..., and pass the integration gate (all of the stage's DoD in the stage worktree) before the next stage; conflicts, integration failures and escalations: execution-plan.md. With more stages to go, setcurrent_stageto the next one instate.jsonand go on in the same session. Offer a fresh session (/clear, thenresume) only when the context has grown large (two stages ran, or many review rounds); it is safe here.
6. Close-out
- Re-run the step 0 baseline command; it must match, else report exactly what changed in the main checkout — do not "fix" it yourself.
- PRs:
independentPRs already exist (§5); list each asmerged,open(reason) or local only.staged: open the single PR from the lastsop-stage/<n>withpr.sh(merge perauto_merge_risk, §6). - Cleanup of merged PRs' worktrees and branches,
worktree.sh sync, and — once every task is cleaned —worktree.sh rundir --archive. No question only withauto_cleanupin the approval, else ask first. Never--force. - Final summary: findings addressed, deferred (and blocked dependents), branches/PRs
with state, what was cleaned up and what is left. If PRs await the user, end with: after
merging them, invoke this skill with
cleanup.
Scripts and layout
Usage, exit codes, worktree paths, provisioning, config overrides: scripts-and-layout.md.