spec-loop
Capability: substrate:framework-dev
Harness: Claude Code, Codex, Cursor, Gemini CLI, OpenCode
A spec-driven build loop for this framework, in the general
Ralph style (run a fresh agent context
against a fixed prompt, repeat), adapted to the framework’s
human-in-the-loop posture. The full write-up is in
docs/spec-driven-development.md;
this is the operator quickstart.
The loop drives a headless agent CLI and is not tied to one harness.
SPEC_LOOP_AGENT picks the CLI (default claude) and SPEC_LOOP_HARNESS
picks its run convention:
claude—claude -p --dangerously-skip-permissions --output-format …, prompt on stdin.codex—codex exec --dangerously-bypass-approvals-and-sandbox -, prompt on stdin.cursor—cursor agent --print --force --trust --workspace … "<prompt>"orcursor-agent --print --force --trust --workspace … "<prompt>".gemini—gemini --yolo --prompt "<prompt>".opencode—opencode run --auto --model … "<prompt>", prompt as a positional argument.
SPEC_LOOP_HARNESS defaults from the agent basename, so
SPEC_LOOP_AGENT=codex, SPEC_LOOP_AGENT=cursor-agent,
SPEC_LOOP_AGENT=gemini, or SPEC_LOOP_AGENT=opencode is usually all that
is needed:
SPEC_LOOP_AGENT=codex tools/spec-loop/loop.sh build 5
SPEC_LOOP_AGENT=cursor-agent tools/spec-loop/loop.sh build 5
SPEC_LOOP_AGENT=gemini tools/spec-loop/loop.sh build 5
SPEC_LOOP_AGENT=opencode SPEC_LOOP_MODEL=anthropic/claude-sonnet-4-5 \
tools/spec-loop/loop.sh build 5
All conventions run the agent non-interactively with permissions
auto-approved; the loop’s safety rails (never push, never open a PR) come from
the OS sandbox, missing push/write credentials, repo hooks, and the loop’s own
guards. Claude also gets per-invocation --disallowedTools hard-deny flags;
the other harnesses rely on their own policy/config plus the external sandbox.
See the SECURITY notes in loop.sh.
Prerequisites
- Runtime: Bash + coreutils (
loop.shis the runner); the spec-side helper tools it drives are Python 3.11+ run viauv. - CLIs:
git(required — must run inside a git checkout),gh(for open-PR duplicate-work checks), and one supported headless agent CLI (SPEC_LOOP_AGENT, defaultclaude; also supportscodex,cursor/cursor-agent,gemini, andopencode). - Credentials / auth:
ghmust be authenticated for the PR checks; the loop is designed to run with no push/write credentials in the environment (it hard-deniesgit pushandghwrites). - Network: Reaches GitHub (via
gh) and the agent/model backend; the agent itself performs the model calls.
The pieces
| File | Role |
|---|---|
specs/ |
The functional description of the product — one spec per area. The desired state the loop reconciles code against. |
IMPLEMENTATION_PLAN.md |
Prioritised work items (the gaps). One work item = one branch = one PR. |
AGENTS.md |
Loop-scoped operational rules (repo map, validation commands, branch + hard-limit rules). |
PROMPT_plan.md / PROMPT_build.md / PROMPT_update.md / PROMPT_consolidate.md |
The per-beat prompts. |
loop.sh |
The runner. |
../spec-inventory/ |
Deterministic compact inventory helper appended to prompts as a routing aid. |
Modes
./tools/spec-loop/loop.sh # build, unlimited iterations
./tools/spec-loop/loop.sh 10 # build, max 10 iterations
./tools/spec-loop/loop.sh plan # gap-analysis → rewrite the plan (no code changes; 1 pass, add N for more)
./tools/spec-loop/loop.sh update # back-fill specs from functionality others contributed (1 pass, add N for more)
./tools/spec-loop/loop.sh consolidate # shrink the plan when it grows too long (1 pass, add N for more)
- plan — compares
specs/against the code and rewritesIMPLEMENTATION_PLAN.md. Plans only; no commits. It also checks open PRs and does not add work items that are already in flight. - build — implements the single highest-priority work item on its own
<slug>branch, validates, and commits there. If the top plan item is already covered by an open PR, it skips to the next uncovered item. - update — the inverse of plan: scans the code for functionality not
yet described by a spec (someone contributed it the normal way) and
brings the specs back in sync, on a
sync-specs-<timestamp>branch. - consolidate — shrinks the plan without losing planned work (build auto-switches to this when the plan grows past ~500 lines).
The two non-negotiables
- A branch per work item. Build/update never commit to the integration base; each carves out its own branch, so every change is one reviewable, revertible PR.
- Never pushes, never opens a PR.
git pushandgh pr createare in.claude/settings.jsonask— the human’s step. Each beat ends at a local commit and prints the exact push +gh pr create --webcommands.
Security
The loop runs the agent with --dangerously-skip-permissions, so it
must be launched inside the project’s sandbox harness, with no
push/write credentials in the environment. The flag bypasses the agent
permission layer (.claude/settings.json deny/ask) but not the OS
sandbox (clean-env + filesystem/network), which stays the real boundary;
for Claude, as defence in depth, the loop also hard-denies git push and
gh via --disallowedTools. Full rationale:
docs/spec-driven-development.md § Security and unattended agent loops.
Stop / configure
- Stop:
Ctrl+C, ortouch STOP(exits after the current iteration). SPEC_LOOP_BASE— branch to fork work items from. Defaults tomain; set it explicitly to build on top of a different branch.SPEC_LOOP_AGENT— supported headless agent CLI or wrapper to run (defaultclaude;codex,cursor,gemini, andopencodeare first-class harnesses).SPEC_LOOP_HARNESS— override the invocation convention when the CLI name does not imply it (claude,codex,cursor,gemini, oropencode).SPEC_LOOP_MODEL— model passed to the agent CLI. Defaults tosonnetfor Claude; Codex/Cursor/Gemini/OpenCode use their configured default unless this is set.SPEC_LOOP_PR_LIMIT— number of open PRs to include in duplicate-work checks (default100).SPEC_LOOP_PLAN_MAX— plan line count that triggers one consolidation round before building (default500). The consolidate beat targets ~300 lines (hysteresis) and runs at most once until the plan drops back under the limit, so a plan that is long because of pending work never re-consolidates in a loop.
Not the RFCs
The specs are the functional description of the code. The
docs/rfcs/ are the separate normative governance
layer — the loop respects them as constraints and never reads or edits
them.