A generic, reusable extraction of the autonomous build loop used to build this project. Drop it
into any project to get a self-verifying, anti-gaming loop that builds a backlog one feature at a
time, proves each works before marking it done, and resumes cleanly across sessions. It runs on
Claude Code or OpenCode (the enforcement engine is tool-agnostic bash+node; only the two agent
prompts are runner-shaped — pick + set up your runner in SETUP.md); your app can be any stack.
What problem it solves: "vibe-coding" agents confidently mark broken work as done and degrade as context fills. This loop fixes both: fresh context every iteration, generation separated from evaluation, state on disk, and evidence-gated marking (a feature can't be marked green unless it re-proves green). Based on the 2026 long-running-harness / "Ralph loop" patterns.
ORCHESTRATOR (main session) IMPLEMENTER (mod-<module>) VERIFIER (integration-verifier)
picks the next feature → builds ONE feature only → judges it (never edits code)
marks / commits / records ← returns a ≤2k summary ← PASS/FAIL + evidence
Separating the builder from the judge is the key lever: a model grading its own work is biased to approve it; a separate, skeptical evaluator is an honest gate.
WAKE (fresh context: read progress log + backlog + git log)
1–3 startup + smoke (init.sh)
4 pick ONE → next-feature.sh / eligible-wave.mjs (SPINE-FIRST, then priority → dep-depth → unblocks → id)
4.5 reuse-first → grep existing code + module doc + <module>-LEARNED.md + ENGINEERING_PRACTICES + code-quality + senior-review + (spine/seam/schema rules)
5 implement → mod-<module> agent builds to the code-quality + senior bar (real wiring at seams)
6 verify → integration-verifier: tamper-check FIRST + rules verify + held_out + e2e + adversarial refute (panel if high-risk) + SENIOR + spine/seam/wiring checks
6.6 quality+security → code-quality + security + senior lens (recorded) → record-lesson.sh into <module>-LEARNED.md
7 record → changelog.sh add (verdict: verify:pass + SENIOR + lessons) + progress line + module doc → mark.sh pass (re-runs verify; REQUIRES the entry — exit 7 without it)
7.5 wave-gate → wave-gate.sh BEFORE commit: verify.sh --quick + corruption guard + SPINE boot + writes a tree-matched RECEIPT; never git add -A
8 regression → regression-due.sh → full sweep when DUE (enforced); cutover-debt.sh → per-wave mock-debt visibility
8.5 live-sweep → next-live.sh → mark.sh <id> live for runnable provisional→live cutovers
9 stop-gate → at NONE: loop-stop-gate.sh (cutover · coverage · spine · seams · wiring · docs-produced · receipt · full gate · e2e) must be GREEN
SLEEP → next wake starts fresh
Scoped runs. Set LOOP_ONLY=<id-prefix> to build just ONE initiative (e.g. LOOP_ONLY=DASH- runs only
the dashboard features), or LOOP_EXCLUDE_MODULES=<csv> to skip modules an autonomous loop shouldn't touch
(e.g. plan-mode-gated money/state modules a human builds attended). Both restrict only what is OFFERED —
dependency resolution and the DAG are unchanged — and both arms (build + live cutover) honor them identically.
What passes between fresh iterations. Canonical read-order: CONTEXT.md.template.
- WHAT to build —
features.json(backlog + optional per-featureimplementation/tests/docs_updateguidance + cutover fields). Loop edits onlypasses/retries/live_passes; the rest is immutable, enforced bymark.sh. Schema:backlog/features.schema.json. - WHAT was done —
progress.md(append-only prose: id · result · evidence · reused-what · Q/S verdict · next) + git history +changelog.jsonl/CHANGELOG.md(structured per-feature: files touched · what · verify · SENIOR · lessons · commit — recovers the per-feature file attribution that bundled wave commits lose, and is queryable:changelog.sh query --file <p>|--module <m>|--id <feat>). - HOW we build —
CLAUDE.md/AGENTS(+@importchain: memory index, rules,ENGINEERING_PRACTICES.md), auto-loaded. - HOW each module works —
docs/modules/*.md+ each module'sCLAUDE.md. - WHY + lessons — ADRs + history, and
ENGINEERING_PRACTICES.md§"Lessons & gotchas" (negative knowledge — what already bit us). The layer is managed (audit-docs.shenforces the floor). - HOW agents improve — per-module
.claude/agents/learned/<module>-LEARNED.md: a living, append-only memory each freshmod-<module>agent reads FIRST, written by the implementer (pitfalls) and the verifier (findings) viarecord-lesson.sh. Themod-*.mdtemplate stays role-stable; this file grows, so agents get better over iterations instead of rediscovering the same gotchas. (audit-docs.shfloor.)
- Evidence-gated mark (+ anti-vacuous) —
mark.sh passre-runs the feature's ownverifyand flipspassesonly on exit 0 AND only if it actually ran — a verify that exits 0 but is all-skipped / collected 0 tests is REFUSED (a skipped or empty test is not evidence).audit-verifies.shstatically flags unconditional-skip smells. (SeeLEARNINGS.md§1.) - Immutable contract, free guidance —
mark.shsnapshots title/behavior/steps/verify/deps/module/priority and the cutover/spine fields (requires_live_wiring/live_verify/spine/cross_schema_of/produces/consumes, so a feature can't dodge the provisional/spine/schema gate by flipping them) and refuses changes; a verify may be strengthened, never weakened, via a tracked feature. The optionalimplementation/tests/docs_updateguidance is NOT frozen (so it can be refined mid-build). - Independent + adversarial verify — a separate fresh-context agent (never the author) tries to
refute every feature;
risk:"high"triggers a 2-of-3 refutation panel DETERMINISTICALLY (a field, not prose inference). The verifier tamper-checks the diff (weakened/skipped verify, hardcoded test inputs,__eq__overrides, mock-without-live) and may run a withheldheld_out_verify(composition the implementer never saw) to defeat validation-saturation. - No weak verify —
audit-verifies.shflags existence-only checks on code modules, mock-only-without- live-wiring, DB-faked schema tests, AND alive_verifythat can false-green (curlwithout-f). - Stop-gate — the run may only end when EVERY gate is green (cutover · coverage · spine · seams · wiring · docs-produced · wave receipt · full gate · e2e), not when the agent thinks it's done.
- No cutover debt (mock-only ≠ done) — a feature whose unit verify mocks a boundary sets
requires_live_wiring:true+ alive_verify; it stays provisional untilmark.sh <id> livere-proves the REAL wiring.loop-stop-gate.shblocks the run from ending while any feature ispasses && requires_live_wiring && !live_passes.audit-verifies.shdetects mock-only verifies (JS/TS and Python);cutover-debt.shsurfaces the debt EVERY wave (not just at the stop-gate) so it can't silently accumulate; the verifier must hit the live path and FAIL on a 404. (SeeEVOLUTION-FROM-LIFEOS.md.) - Senior bar + self-improving agents — "done" requires clearing a senior code-review lens
(
rules/senior-review.md:SENIOR ≥ 3.5/5, no dimension ≤ 2), not just a green test. Every pitfall or verifier finding is written back viarecord-lesson.shinto the owning module's<module>-LEARNED.md, which the next agent in that module is required to read — so the same class of bug can't recur and the agents get better each wave.audit-docs.shenforces every module carries a LEARNED file. - The system must COMPOSE, not just unit-pass (integration spine) — defeats "294 features green, app
won't start." The walking skeleton (composition root, boot, one e2e happy-path, e2e harnesses) is tagged
spine:true, must berequires_live_wiring:truewith a real boot/e2elive_verify(never unit-only), and is built FIRST.audit-spine.shFAILs a spine feature with no live_verify;wave-gate.shre-runs every spinelive_verifyeach wave so a wiring regression can't be committed; the stop-gate blocks while any spine feature is notlive_passes. Cross-tier contracts are SEAMS — producer + consumer must AGREE (seams.json+audit-seams.shcatch theticks.<symbol>vsticksclass). (SeeLEARNINGS.md§9.) - Reward-hacking blocked + no vacuous gates —
mark.sh passREFUSES (exit 6) a diff that weakens an already-passed feature's test (the #1 documented hack);audit-spine.shFAILs a backlog with no spine (a guarantee bypassable by omission is none); an impossible feature is recordedmark.sh <id> blocked(surfaced for a spec decision) rather than force-greened; parallel waves are race-safe (a backlog write-lock). The two pickers share one selector so build order is reproducible. (SeeLEARNINGS.md§10–13.) - Documentation is HARD-GATED, not honor-system — a feature can't go green without its docs:
mark.sh passREFUSES (exit 7) unless achangelog.jsonlentry records the verdict (verify:pass+SENIOR ≥ 3.5);audit-docs-produced.shre-checks that + aprogress.mdline + lessons + (when flagged) an ADR /ENGINEERING_PRACTICES.mdupdate every wave;audit-docs.shFAILs a module doc gone stale vs its source. - Wiring enforced beyond the compiler (4 layers) — type checker (signatures) · spine (main path) ·
seams (
audit-seams.sh: producer/consumer agree,produces/consumes↔seams.json, orphan/dangling/drift) · wiring invariants (audit-wiring.sh: every defined unit is REGISTERED in its consumer — the orphan-handler class the type checker is blind to). The verifier flags a registry-bypassing verify. (rules/wiring-contracts.md) - The gates can't be skipped (meta-gates) — the verifier verdict is required at mark-time (10);
wave-gate.shwrites a tree-matched receipt thatloop-stop-gate.sh(step 0.9) requires, and the optional Claude Code hooks (hooks/) block agit commitwithout a fresh receipt + run the stop-gate on done. So "I ran the gates" is proven, not claimed (OpenCode: the receipt + verdict enforce it without hooks).
loop-kit/
README.md ← this file (architecture + mechanism + extraction audit)
install.sh ← one-command install / --update (overwrites machinery, never clobbers your edits)
SETUP.md ← choose your runner (Claude Code / OpenCode) + wire the agent layer — START HERE
INSTALL.md ← step-by-step: the runner-agnostic mechanics (backlog · gates · drive the loop)
MIGRATION.md ← upgrading an OLDER loop-kit setup: exact changes + what newly fails + opt-outs
EVOLUTION-FROM-LIFEOS.md ← the spec for the richer-metadata + cutover-closure upgrades (round history)
CONTEXT.md.template ← the canonical per-iteration read-order (5-layer knowledge manifest)
loop-iteration.md ← the generic per-iteration contract
AUTHORING.md ← how to write the backlog + sequence the build (the author's responsibilities)
SCAFFOLDING.md ← how to set up the repo BEFORE the loop (walking skeleton, injected ports, guardrails)
LEARNINGS.md ← hard-won failure modes + the mechanism that now defends each (READ THIS)
rules/code-quality.md ← the quality bar (reuse/cleanup/security/tests/docs)
rules/security.md ← the security bar (no secrets, sanitize input, parameterized queries, least privilege)
rules/senior-review.md ← the senior code-review lens (8-dim rubric; the verifier scores it)
rules/test-isolation.md ← autouse-reset pattern so the full suite is deterministic
rules/schema-parity.md ← no mock-only verify for cross-schema reads/writes (read the real schema)
rules/integration-spine.md ← the walking skeleton: the system must boot+compose end-to-end, live
rules/seam-contracts.md ← seam = contract parity (producer/consumer agree on a key; token == seam name)
rules/wiring-contracts.md ← wiring = registration completeness (every defined unit is registered) + the 4 wiring layers
agents/implementer.md ← generic mod-<module> template (+ metadata + LEARNED-file guidance)
agents/integration-verifier.md← generic verifier (separate evaluator, adversarial + panel + mock-detect + SENIOR lens)
agents/learned/MODULE-LEARNED.md.template ← per-module living-lessons file (agent self-improvement)
hooks/ ← optional Claude Code hooks: precommit (wave-gate receipt) + Stop (loop-stop-gate)
opencode/opencode.json ← OpenCode config template (instructions globs + permissions + agent block)
opencode/agents/*.md ← OpenCode-format agent templates (mode:subagent + permission map)
backlog/features.schema.json ← backlog JSON schema (incl. metadata, live-wiring, spine, risk, held_out fields)
backlog/features.example.json ← a tiny working example backlog
backlog/coverage-map.example.json ← seed for audit-coverage (anchors a backlog must cover)
backlog/seams.example.json ← seed for audit-seams (cross-tier contracts; token == seam name)
backlog/wiring.example.json ← seed for audit-wiring (registry/dispatch invariants: producer_glob → consumer)
scripts/
eligible-core.mjs ← the SINGLE BUILD-arm selector (filter + spine→priority→depth→unblocks→id sort)
next-feature.sh ← thin wrapper over eligible-core (one id) — generic
eligible-wave.mjs ← thin wrapper over eligible-core (one feature per module, conflict-free wave)
next-live.sh / eligible-live.mjs ← LIVE arm: next/all provisional→live cutovers runnable now
mark.sh ← evidence-gated marker — pass|live|fail|blocked|reset (REFUSES vacuous + test-tamper + no-verdict; backlog-locked)
wave-gate.sh ← per-wave gate: verify.sh --quick + regression-due(auto-mark) + corruption guard + SPINE boot + writes a receipt
audit-spine.sh ← integration-spine gate (spine features must be live-wired; system must compose)
audit-seams.sh ← seam contract parity (produces/consumes ↔ seams.json; orphan/dangling/drift; no-op if none)
audit-wiring.sh ← wiring invariants: every defined unit is registered in its consumer (no-op if no wiring.json)
audit-docs-produced.sh ← per passed feature: changelog entry + verdict (verify:pass+SENIOR) + progress + lessons + flagged ADR/pattern
regression-due.sh ← enforced regression cadence (generic)
merge-backlog.mjs ← PRE-LOOP backlog validation (unique ids · deps resolve · DAG acyclic · mod-agent exists)
audit-coverage.sh ← prove the backlog covers the whole build (coverage-map anchors); no-op if no map
loop-stop-gate.sh ← exit gate — cutover + coverage + spine + seams + wiring + docs-produced + receipt + full gate + e2e
cutover-debt.sh ← mock-debt register; BLOCKING vs live_human_gated DEFERRED + external-input surfacing
record-lesson.sh ← append a deduped lesson to a module's LEARNED file (agent self-improvement)
changelog.sh ← per-feature code-evolution log (add|render|link|query) → changelog.jsonl + CHANGELOG.md
audit-verifies.sh ← verify-rigor auditor: existence-only + mock-only + DB-fake + unconditional-skip + weak live_verify
audit-docs.sh ← knowledge-layer auditor (CLAUDE.md + LEARNED + mod-agent per backlog module; @imports)
loop-status.sh ← one-line status incl. provisional (live-pending) debt
init.sh.template ← real proven-infra smoke (fill <YOUR ...> markers)
verify.sh.template ← your project's rules gate (lint+typecheck+unit = req fast slice; --quick lane)
Generic scripts read the backlog via LOOP_BACKLOG (default docs/build/features.json).
Each kit file was extracted from a real running build-loop project, with all domain specifics replaced by
<PLACEHOLDERS> / EDIT markers so the kit is project-agnostic. (The war-story examples in LEARNINGS.md
and the rules are kept concrete for teaching value but are illustrations, not requirements.)
| Kit file | Source file | Genericized |
|---|---|---|
scripts/next-feature.sh |
scripts/next-feature.sh |
backlog path → LOOP_BACKLOG env (logic identical) |
scripts/mark.sh |
scripts/mark.sh |
backlog path → env (evidence-gating + immutability identical) |
scripts/regression-due.sh |
scripts/regression-due.sh |
backlog path → env; state file beside backlog |
scripts/loop-stop-gate.sh |
scripts/loop-stop-gate.sh |
verify.sh + playwright → EDIT markers |
scripts/audit-verifies.sh |
scripts/audit-verifies.sh |
code-module list + behavioral regex → EDIT (added pytest/go/cargo) |
scripts/verify.sh.template |
scripts/verify.sh |
docker/postgres/npm checks → <YOUR CMD> placeholders (kept the per-check log + req/opt harness) |
loop-iteration.md |
scripts/loop-iteration.md |
master.md / Gmail / domain rules → <DOMAIN INVARIANTS — EDIT> |
rules/code-quality.md |
.claude/rules/code-quality.md |
removed job-search references |
agents/implementer.md |
.claude/agents/mod-*.md (common shape) |
module/paths/guardrails → placeholders |
agents/integration-verifier.md |
.claude/agents/integration-verifier.md |
stack-agnostic wording |
backlog/features.schema.json |
docs/build/features.json meta.schema + field usage |
formalized as JSON Schema |
Not extracted (project-specific, rebuild per project): scripts/init.sh (smoke — your stack's
containers/deps), the concrete verify.sh checks, the per-module implementer files, the actual backlog.