title: Spec-loop runner status: experimental kind: feature mode: infra source: > tools/spec-loop/README.md, tools/spec-loop/loop.sh, tools/spec-loop/lib.sh, tools/spec-loop/tests/test_runner_fixtures.sh, tools/spec-loop/PROMPT_plan.md, tools/spec-loop/PROMPT_build.md, tools/spec-loop/PROMPT_update.md, tools/spec-loop/PROMPT_consolidate.md, and docs/spec-driven-development.md. acceptance:
tools/spec-loop/.last-sync is owned by the runner, not by the prompt.The spec-loop runner is the framework's local, review-first development loop. It runs a headless agent CLI against fixed prompts, one iteration at a time, to compare specs with the working tree, produce planned changes, back-fill specs after normal contributions, or consolidate a long implementation plan.
The loop is not an autonomous merge system. Its boundary is local branch creation: it may edit, validate, and commit locally, but it never pushes and never opens a pull request.
tools/spec-loop/loop.sh — Bash runner for the four beats.tools/spec-loop/lib.sh — deterministic prompt assembly, harness command rendering, agent launch, and .last-sync marker helpers used by the runner and fixture tests.tools/spec-loop/tests/test_runner_fixtures.sh — deterministic fixture tests for prompt assembly, harness command construction, and .last-sync marker helpers; executed by spec-validate.tools/spec-loop/PROMPT_plan.md — gap-analysis prompt that rewrites IMPLEMENTATION_PLAN.md.tools/spec-loop/PROMPT_build.md — implementation prompt for exactly one work item.tools/spec-loop/PROMPT_update.md — spec back-fill prompt for functionality that landed outside the loop.tools/spec-loop/PROMPT_consolidate.md — plan-size reduction prompt.tools/spec-loop/AGENTS.md — loop-specific operational context: repository map, validation commands, branch rules, hard limits, and commit rules.tools/spec-loop/README.md — operator quickstart.docs/spec-driven-development.md — full explanation of the loop's posture and lifecycle.plan compares specs to code and updates the implementation plan without committing. build implements one uncovered work item. update compares code to specs and back-fills the spec directory. consolidate shrinks the implementation plan without dropping planned work.<slug> branch off SPEC_LOOP_BASE and commits exactly one work item there. Update work creates a uniquely named sync-specs-<timestamp> branch. Consolidate commits only the plan file on the control branch.git push and gh pr create; each successful build/update beat prints the human-run push and gh pr create --web commands instead.TOOLING_REF is captured before checkout. When the integration base does not carry the spec-loop files, the runner tells the agent to read prompts/specs/plan from the control branch with git show, while implementing product changes on the work branch.tools/spec-loop/.last-sync records the base SHA last synced by the update beat. The runner reads it to append incremental-scope guidance, then amends or creates a marker commit after the agent finishes. Prompts must not instruct the agent to edit the marker.IMPLEMENTATION_PLAN.md exceeds SPEC_LOOP_PLAN_MAX, then builds even if planned work alone keeps the file over the threshold. The latch resets once the plan drops below the threshold.Every supported harness must provide the same loop-level behaviour: run one non-interactive agent iteration in the repository root, receive the assembled prompt, allow local edits/validation/commits under the external sandbox, and stop without pushing or opening a PR.
| Harness | Prompt transport | Cwd contract | Unattended flag | Model override | Output mode | Extra denial |
|---|---|---|---|---|---|---|
| Claude Code | stdin to claude -p | launched from repo root | --dangerously-skip-permissions | --model | --output-format | --disallowedTools denies push and gh |
| Codex | stdin to codex exec - | --cd "$ROOT" | --dangerously-bypass-approvals-and-sandbox | --model | --json for stream JSON | external sandbox and exec policy |
| Cursor | positional prompt to cursor agent --print or cursor-agent --print | --workspace "$ROOT" | --force --trust | --model | --output-format | external sandbox and Cursor policy |
| Gemini CLI | --prompt "<prompt>" | launched from repo root | --yolo | --model | default CLI output | external sandbox and Gemini policy |
| OpenCode | positional prompt to opencode run | launched from repo root | --auto | --model | --format json for stream JSON | external sandbox and OpenCode policy |
| Kiro | positional prompt to kiro-cli chat | launched from repo root | --no-interactive | agent config (.kiro/agents) | plain text only | external sandbox and the agent-guard --kiro hook |
SPEC_LOOP_AGENT chooses the CLI. SPEC_LOOP_HARNESS chooses the invocation convention and defaults from the agent basename. Adding a new harness means extending this matrix, documenting the safety boundary, and updating loop.sh in the same change.
docs/rfcs/; RFCs remain the separate governance layer.loop.sh accepts plan, build, update, and consolidate, plus a bare numeric build count, and rejects unknown modes or non-numeric iteration counts.SPEC_LOOP_BASE, create a non-base branch, and stop if a commit lands on the base..last-sync when present, but the runner remains the only writer of .last-sync.loop.sh.<slug> branches; update uses sync-specs-<timestamp> branches.bash -n tools/spec-loop/loop.sh shellcheck tools/spec-loop/loop.sh shellcheck tools/spec-loop/lib.sh tools/spec-loop/tests/test_runner_fixtures.sh uv run --project tools/spec-validator --group dev spec-validate