Capability: substrate:framework-dev
Harness: Claude Code, Codex, Cursor, Antigravity, Pi, Goose, OpenCode, Kiro, Grok
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>" or cursor-agent --print --force --trust --workspace … "<prompt>".antigravity — agy -p "<prompt>" --output-format …, with --effort when SPEC_LOOP_EFFORT is set and never --dangerously-skip-permissions.pi — pi -p "<prompt>", with --mode json for the JSON output formats and --thinking when SPEC_LOOP_EFFORT is set. Selected only by the exact agent name pi.goose — goose run --no-session -i <prompt file>, with --model when set and no effort flag. Selected by the exact agent name goose.opencode — opencode run --auto --model … "<prompt>", prompt as a positional argument.grok — grok --always-approve --cwd … --prompt-file …, with explicit deny rules for remote mutation.SPEC_LOOP_HARNESS defaults from the agent basename, so SPEC_LOOP_AGENT=codex, SPEC_LOOP_AGENT=cursor-agent, SPEC_LOOP_AGENT=agy, SPEC_LOOP_AGENT=pi, SPEC_LOOP_AGENT=opencode, or SPEC_LOOP_AGENT=grok 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=opencode SPEC_LOOP_MODEL=anthropic/claude-sonnet-4-5 \ tools/spec-loop/loop.sh build 5
SPEC_LOOP_AGENT=grok tools/spec-loop/loop.sh build 5
Antigravity CLI: Antigravity CLI (agy) replaced Gemini CLI, which Google retired for individual accounts on 2026-06-18. The antigravity profile (selected by an agent name containing agy or antigravity) never passes --dangerously-skip-permissions. Headless runs keep Antigravity's request-review permission mode: a tool call that needs approval is soft-denied, the run still exits 0, and the JSON envelope lists it under denied_actions. Unattended build iterations that need edits or shell calls therefore cannot complete under this profile. An operator who deliberately wants auto-accepted workspace edits wraps agy --mode accept-edits in their own SPEC_LOOP_AGENT script; the runner does not add it. See the Antigravity runner notes.
Pi: Pi has no approval prompts of its own and the pi profile adds no approval flag. Magpie‘s user-scope Pi extension refuses every command that would need approval when there is no UI, so an iteration that needs a push or a mutating gh call cannot complete unattended under this profile, and plain bash runs in the extension’s OS sandbox. See the Pi runner notes.
Goose: the goose profile adds no approval flag either; Goose‘s mode comes from its settings. A headless run cannot answer approve-mode prompts, and outside approve mode Magpie’s Goose hook blocks everything that would need confirmation, so a push or a mutating gh call cannot run unattended. See the Goose runner notes.
All conventions run the agent non-interactively. The profiles other than Antigravity, Pi and Goose request automatic approval; their 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 gets per-invocation --disallowedTools hard-deny flags. Grok gets equivalent per-invocation --deny rules for git push and gh. The remaining harnesses rely on their own policy/config plus the external sandbox. See the SECURITY notes in loop.sh.
loop.sh is the runner); the spec-side helper tools it drives are Python 3.11+ run via uv.git (required — must run inside a git checkout), gh (for open-PR duplicate-work checks), and one supported headless agent CLI (SPEC_LOOP_AGENT, default claude; also supports codex, cursor / cursor-agent, agy, pi, goose, opencode, kiro-cli, and grok).gh must be authenticated for the PR checks; the loop is designed to run with no push/write credentials in the environment (it hard-denies git push and gh writes).gh) and the agent/model backend; the agent itself performs the model calls.| 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. |
./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)
specs/ against the code and rewrites IMPLEMENTATION_PLAN.md. Plans only; no commits. It also checks open PRs and does not add work items that are already in flight.<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.sync-specs-<timestamp> branch.git push and gh pr create are in .claude/settings.json ask — the human's step. Each beat ends at a local commit and prints the exact push + gh pr create --web commands.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.
Ctrl+C, or touch STOP (exits after the current iteration).SPEC_LOOP_BASE — branch to fork work items from. Defaults to main; set it explicitly to build on top of a different branch.SPEC_LOOP_AGENT — supported headless agent CLI or wrapper to run (default claude; codex, cursor, agy, pi, goose, opencode, kiro-cli, and grok are first-class harnesses).SPEC_LOOP_HARNESS — override the invocation convention when the CLI name does not imply it (claude, codex, cursor, antigravity, pi, goose, opencode, kiro, or grok).SPEC_LOOP_MODEL — model passed to the agent CLI. Defaults to sonnet for Claude; Codex/Cursor/Antigravity/Pi/Goose/OpenCode/Grok use their configured default unless this is set.SPEC_LOOP_EFFORT — reasoning-effort band: low, medium, or high. Unset means no effort flag is passed. Mapped per harness (Claude/Kiro/Antigravity --effort, Pi --thinking, Codex -c model_reasoning_effort=…, OpenCode --variant; Cursor has no knob and silently omits it). Framework high maps to each CLI's top value (max / xhigh); low/medium are not always pass-through (OpenCode low → minimal). Exact values are in the effort table in specs/spec-loop-runner.md.SPEC_LOOP_PR_LIMIT — number of open PRs to include in duplicate-work checks (default 100).SPEC_LOOP_PLAN_MAX — plan line count that triggers one consolidation round before building (default 500). 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.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.