SPEC-0020: /loop Autonomous Mode for /sdd:work and /sdd:review
Overview
Defines how /sdd:work and /sdd:review cooperate with the runtime /loop skill so users can grind a backlog (or watch a single PR) autonomously without losing the user-in-the-loop preference. The contract is opt-in via a --loop flag on each skill: the runtime re-invokes the skill, and the skill enforces stop conditions, concurrency, user-prompt gates, budget ceilings, and inter-iteration telemetry. /loop itself is unchanged. In addition to the loop iteration mechanics, this spec governs the post-PR chain pattern that hands off PR follow-up from /sdd:work to /sdd:review (one architectural round) and then to /autofix-pr (Claude Code built-in for ongoing CI/maintenance), per 📝 ADR-0030.
This spec realizes 📝 ADR-0028 by translating its sub-decisions into RFC 2119 requirements. It covers the CLI surface for both skills, the twelve stop conditions (iteration / PR / wall-clock / dollar budgets, repeated failure, dependency cycle, user interrupt, lockfile contention, qmd-unreachable, and prior-gate stop), the lock-and-skip concurrency model with PID liveness as the sole staleness signal, the six AskUserQuestion gates (backlog drift, ambiguous criteria, budget escalation, post-feedback merge, force-unlock, repeated failure), the on-disk artifacts (.sdd/loop/\{skill\}.lock, .budget.json, .history.jsonl), the resume contract for crash recovery, and the single-PR /sdd:review --loop --pr <N> watch mode.
This spec is not web-facing. No public HTTP surface is created; 📝 ADR-0018 security-by-default does not apply. All artifacts are project-local under .sdd/loop/ and treated as treat-as-secret by default per the sensitive-content note in this spec's "Telemetry Schema" requirement.
Requirements
Requirement: Loop Mode Opt-In
/sdd:work and /sdd:review MUST accept a --loop flag that opts into autonomous mode. The flag MUST be off by default; absence of the flag MUST preserve the pre-existing skill behavior unchanged. Loop mode MUST NOT modify the runtime /loop skill — re-invocation cadence remains /loop's concern; everything inside an iteration is the wrapped skill's concern.
Scenario: User invokes the skill without --loop
- WHEN a user runs
/sdd:work SPEC-0019with no--loopflag - THEN the skill MUST behave exactly as it does today (single invocation, no lockfile, no budget file, no history line)
- AND the skill MUST NOT create
.sdd/loop/or any artifact under it
Scenario: User invokes the skill with --loop under /loop
- WHEN a user runs
/loop /sdd:work --loop - THEN the runtime
/loopMUST handle iteration scheduling - AND the wrapped skill MUST enter the autonomous-mode contract (stop-condition evaluation, lockfile, budget, telemetry) on every tick
Requirement: CLI Surface for Loop Controls
When --loop is set, both skills MUST accept the following flags. All flags MUST be optional with the documented conservative defaults.
| Flag | Applies to | Default | Purpose |
|---|---|---|---|
--max-iterations N | both | 5 | Iteration ceiling across the loop run |
--max-prs N | both | 20 | Distinct-PR ceiling across the loop run |
--max-minutes N | both | 60 | Wall-clock ceiling across the loop run |
--max-dollars N | both | 25 | Dollar-cost ceiling; 0 disables the cost ceiling |
--lock=\{skip|wait|force\} | both | skip | Concurrency mode on lockfile contention |
--resume | both | off | Recover state from the most recent history.jsonl line |
--budget-file PATH | both | .sdd/loop/\{skill\}.budget.json | Override the budget-file location |
--pr N | review | none | Single-PR watch mode (see "Single-PR Review Loop Semantics") |
Budgets MUST be inclusive across the entire loop run, not per-iteration.
Scenario: Conservative defaults applied when no flags are passed
- WHEN a user runs
/loop /sdd:work --loopwith no budget flags - THEN the skill MUST apply:
max_iterations=5,max_prs=20,max_minutes=60,max_dollars=25,lock=skip - AND these defaults MUST be recorded in
budget.jsonon first write so resume cannot silently change them
Scenario: User widens a budget explicitly
- WHEN a user runs
/loop /sdd:work --loop --max-prs 50 --max-dollars 100 - THEN the skill MUST honor those values
- AND the recorded
budget.jsonMUST reflect them as the active ceilings
Requirement: Backlog-Empty Stop (Condition #1)
/sdd:work --loop MUST stop when the filtered backlog (unblocked, unworked, in-scope issues) is empty on entry. The skill MUST emit a final report naming the empty queue as the stop cause and MUST release the lockfile.
Scenario: Loop run completes the backlog
- WHEN
/sdd:work --loopenters iteration N and the discovery phase returns zero workable issues - THEN the skill MUST stop the loop, emit a final report ("Backlog empty — N iterations used, M PRs touched"), and release the lockfile
- AND MUST NOT signal
/loopto schedule another tick
Requirement: Terminal-PR Stop (Condition #2)
/sdd:review --loop --pr <N> MUST stop when the target PR reaches a terminal state: merged, closed, or labeled with the project's configured do-not-merge label.
Scenario: PR is merged between iterations
- WHEN
/sdd:review --loop --pr 142enters iteration N and PR #142's tracker state ismerged - THEN the skill MUST stop the loop and report "PR #142 reached terminal state: merged"
- AND MUST release the lockfile and not signal another tick
Requirement: Iteration Budget Stop (Condition #3)
The loop MUST stop when iterations_used >= max_iterations. The check MUST run on entry to each tick, after lockfile acquisition and before the gate block. The recorded stop cause MUST be iteration_budget.
Scenario: Iteration ceiling reached
- WHEN
iterations_usedwould become 6 on the 6th tick of a run withmax_iterations=5 - THEN the skill MUST stop on entry to that tick and record
stop_conditions_fired: ["iteration_budget"]in the final history line
Requirement: PR-Touch Budget Stop (Condition #4)
The loop MUST stop when len(prs_touched) >= max_prs. The set MUST be deduplicated — a PR re-reviewed across two iterations counts once. For /sdd:review --loop --pr <N> the dimension MUST be inactive (see "Single-PR Review Loop Semantics") and MUST NOT trigger this stop.
Scenario: PR-touch ceiling reached mid-iteration
- WHEN
/sdd:work --loop --max-prs 5would open a sixth distinct PR in an iteration - THEN the skill MUST stop the iteration after the fifth PR opens, record the cause as
prs_touched_budget, and not schedule another tick
Scenario: Single-PR review mode does not trigger this stop
- WHEN
/sdd:review --loop --pr 142runs for ten iterations - THEN
prs_touchedMUST remain["#142"]and MUST NOT trip condition #4
Requirement: Wall-Clock Budget Stop (Condition #5)
The loop MUST stop when minutes_elapsed >= max_minutes. The clock MUST start at the recorded started_at in budget.json and MUST persist across --resume.
Scenario: Wall-clock ceiling reached
- WHEN
/sdd:review --loop --max-minutes 30enters its 5th tick at minute 31 of the run - THEN the skill MUST stop on entry and record
stop_conditions_fired: ["wall_clock_budget"]
Requirement: Repeated-Failure Stop (Condition #6)
The loop MUST detect when the same issue or PR has failed in two consecutive iterations with the same root-cause signature. On detection the skill MUST trigger the "Repeated Failure" AskUserQuestion gate (see "AskUserQuestion Gates") rather than silently halt. If the user answers stop, the loop halts; otherwise it continues per the user's choice.
Scenario: Same issue fails twice with the same error
- WHEN issue #44 fails iteration 2 with root cause "tests failing in module X" and fails iteration 3 with the same root cause
- THEN on iteration 3's exit the skill MUST fire the Repeated Failure gate naming #44 and the root cause
- AND the user's answer MUST be recorded in the next iteration's
gates[]entry
Requirement: Dependency-Cycle Stop (Condition #7)
/sdd:work --loop MUST stop when issue-dependency analysis (per SPEC-0015 Layer 2 machine-readable dependencies) detects a cycle in the workable backlog. The skill MUST surface the cycle's edges and request manual resolution; it MUST NOT attempt to break the cycle automatically.