A universal loop bootstrap scaffold — creation-phase bootstrap for one new feature in any project, upgrading "let the agent write, verify, and loop on its own" from prompt-based self-discipline to mechanism-enforced.
loops-init is a Claude Code skill. It only generates scaffolding: extract a spec, generate the builder/checker agents, generate anti-cheat hooks and idempotently merge them into the project's .claude/settings.json, lay down the DoD and the PROGRESS.md execution ledger, then STOP — hand off to human review and restart the session, after which the native Claude Code /goal runs the builder↔checker wave loop.
It does not dispatch agents in this session, does not run the loop, and does not build its own orchestrator.
When you hand a feature to an automated "agent implements → agent checks → loop again if it fails" cycle, the biggest risk isn't that the agent can't write code — it's that it will weaken the check just to make it pass: editing tests, deleting assertions, loosening the checker's criteria, and finally reporting a fake ALL GREEN.
Merely telling the agent "don't cheat" via prompt is not enough (METR's experiments show such prompt-only constraints are 70–95% ineffective). loops-init's answer is three orthogonal layers of defense, produced all at once:
| Layer | Role | Implementation |
|---|---|---|
| ① Verification layer | builder writes, checker verifies — separation of duties | the checker's tools: field has no Write/Edit = physically read-only |
| ② Anti-cheat layer | enforce at the mechanism layer (not via prompt) | three hooks: PreToolUse / Stop / PostToolUse |
| ③ Loop-control layer | deterministic turn cap + continuous main agent with no amnesia + per-round evaluator feedback | native Claude Code /goal |
Core tenet: the checker physically cannot write code; the builder physically cannot touch the "loop-control source of truth" (spec / agents / hooks / settings / ledger). The path of weakening checks is sealed at the mechanism layer, rather than relying on the agent's self-discipline.
loops-init's loop design — "Think → Act → Observe → Verify → Evolve → Repeat", when to stop, how to verify, how to recover — draws directly on the Loop / Harness engineering material below. This project takes their thesis that "the gap between a hobby project and a production system lies in Harness engineering (including the Loop)" and turns it into a mechanism-enforceable bootstrap scaffold.
- Loop Engineering — Addy Osmani (addyosmani.com)
- Loop Engineering — Firecrawl (firecrawl.dev)
- What Is the AI Agent Loop? — Oracle (blogs.oracle.com)
- Harness Engineering — OpenAI (openai.com)
- Harness Engineering for Coding Agent Users — Martin Fowler (martinfowler.com)
- Agentic Loops: From ReAct to Loop Engineering (datasciencedojo.com)
- Loop Engineering for AI Agents (Memory-First) — Mem0 (mem0.ai)
- Agentic Harness Engineering — arXiv:2604.25850
- From Agent Loops to Structured Graphs — arXiv:2604.11378
Phase 1 — this skill (one session)
Step0 align → 1 extract spec → 2 fill agents → 3 probe infra
→ 4 gen hooks + merge settings → 5 golden compare → 6 write & [STOP]
│
▼ (human reviews spec/agents, then restarts the session)
│
Phase 2 — human + native /goal
paste the /goal driver → builder↔checker loop, wave by wave, until ALL GREEN
Why you must stop and restart: Claude Code loads .claude/agents/*.md and hooks at session start. The agent/hook just Written in this session can't be invoked / isn't in effect in the current session — only a restart brings them online. So "write files → stop → human review → restart" is a hard requirement, not optional.
Claude Code ships with two loop-related built-in skills:
/loopand/goal. The three are not at the same layer and aren't competitors — once you see the division of labor, you see which gap loops-init fills.
native /loop |
native /goal |
loops-init | |
|---|---|---|---|
| Essence | a session-level scheduler that re-triggers on a time interval | a loop engine that iterates toward a completion condition | a bootstrap-scaffold generator for the loop (doesn't run the loop itself) |
| Drive signal | the clock (/loop 5m …; omit the interval → dynamic 1 min–1 hr) |
completion condition + an evaluator (a small model, usually Haiku) that judges and gives feedback each round | —— |
| When it stops | manual stop / expires after 7 days | the condition is judged satisfied by the evaluator; no built-in turn cap, you can explicitly add "stop after N rounds" | stops as soon as files are written; hands off to human review + restart |
| Cross-round context | each trigger is relatively independent | the main agent never loses memory throughout | —— |
| Who does the work | the single prompt/command you gave | a single main agent iterating across rounds (no native subagent dispatch) | generates the two agents builder/checker + anti-cheat hooks + DoD |
| Verification / anti-cheat | none | none (only judges whether the condition is met) | the core: physically read-only checker + mechanism-layer hooks that seal off "weakening checks" |
loops-init complements /goal, it doesn't replace it: in Phase 2, the loop that actually drives builder↔checker is the native /goal. The "driver string" loops-init emits is essentially a carefully constructed /goal goal — it fills in, one by one, the blanks /goal leaves open:
- turns
/goal's abstract condition into a mechanically verifiable DoD (only "all DoDs judged ALL GREEN by the checker, with no check weakened" counts as done); - turns
/goal's "single main agent does the work" into dispatching builder to implement and checker to verify each round (the subagent orchestration is written into the goal string by loops-init, not a native/goalcapability); - supplies the turn cap
/goallacks, explicitly adding "stop after 8 rounds / stop on the same failure twice"; - adds the hooks
/goaldoesn't care about at all — defending against cheating at the mechanism layer.
In one line: /goal provides the engine ("iterate until the condition is met + evaluator + no amnesia"), and loops-init provides the "fuel and guardrails fed to the engine" — the verification criteria, the per-round division of labor, the anti-cheat boundary. /loop (time-based repetition) has essentially no overlap with this goal iteration: you wouldn't use /loop to run builder↔checker.
Step0 align → Step1 extract spec → Step2 fill agents → Step3 probe infra → Step4 gen hooks + merge settings → Step5 golden compare → Step6 stop (emit /goal)
▲ no entry to Step2 until spec on disk ▲ probing decides conditional gen ▲ [MANDATORY] live-test agent_type before going online
Spec must precede agents: the multi-line blocks Step2 fills into the agent (naming / existing assets / coverage targets / semantic points) are excerpted directly from the spec. Without an actually-extracted spec, the agent is an empty shell — and an empty-shell checker will declare a false ALL GREEN.
| Step | What it does | Output / key action |
|---|---|---|
| Step 0 Align with the source of truth | Bash auto-probe + two AskUserQuestion batches (hard prerequisites / soft defaults); choose builder/checker models | <SPEC_DIR>/00-alignment.md. Source of truth "none yet" → STOP |
| Step 1 Extract spec draft | Mechanical layer dumps .xlsx/.xls → judgment layer maps by "source → spec md"; four kinds of doubt surfaced mechanically, not adjudicated for the user |
each spec md + _manifest.json coverage ledger |
| Step 2 Fill agent drafts | Read the template masters, intelligently fill {{...}}, preserve non-placeholder sentences verbatim; archive conflicting old agents |
agents/builder.md checker.md + dod/wave*.yaml + PROGRESS.md |
| Step 3 Probe infra | probe-infra.sh → HAS_GIT/HAS_TEST/CAN_COMPILE/OS/SHA_CMD; probe-hooks-enabled.sh → whether hooks can actually run |
decides subsequent conditional generation |
| Step 4 Generate hooks + merge settings | Live-test agent_type before going online; render.sh renders → merge-settings.sh idempotently merges |
.claude/hooks/*.sh + registered into settings.json |
| Step 5 Golden comparison | Re-render each hook with canonical.values, diff verbatim against fixtures/hooks.golden/ |
mismatch = template tampered → stop and alarm |
| Step 6 Write and stop | Print the delivery report + emit two /goal driver strings → end the turn |
the only endpoint, never runs the loop |
| Artifact | Location | Role |
|---|---|---|
builder agent |
.claude/agents/builder.md |
implements / fixes only; has Write/Edit but can't touch protected paths |
checker agent |
.claude/agents/checker.md |
checks only, no Write/Edit (physically read-only); judges by rule-id across 5 categories (coverage / naming / style / reference / semantics) |
| anti-cheat hooks | .claude/hooks/{loop-guard,loop-stall,post-audit}.sh |
see table below |
| DoD | <SPEC_DIR>/dod/wave*.yaml |
each carries a rule id + a pre-assertion that currently must FAIL + a post-assertion with content-level grep |
| execution ledger | <SPEC_DIR>/PROGRESS.md |
wave-level state machine pending→in-progress→GREEN|ESCALATED; only the main /goal session can write |
| hook | event | responsibility |
|---|---|---|
loop-guard |
PreToolUse |
the unconditional anti-cheat core. Routes by agent_type: allow the main session; block any sub-agent writing to protected paths (spec / agent / CLAUDE.md / settings / hooks / .loop-state) — covering Write/Edit-family tools and Bash bypass-writes |
loop-stall |
Stop |
idle detection. Content-hash snapshot of the watched file set; N consecutive rounds with no change → decision:block forces a stop, instead of burning to the turn cap |
post-audit |
PostToolUse |
traces to audit.log (fires after the write, for post-hoc traceability only) |
Conditional generation matrix (degrades with project capability, but the anti-cheat core is never absent)
| Project capability | loop-guard (anti-cheat) | loop-stall (idle) | post-audit (audit) | DoD |
|---|---|---|---|---|
| Unconditional | ✅ full | — | — | — |
| Has git | ✅ | Upgrade: commit + git diff HEAD to detect empty diff |
Upgrade: git diff count assertions / detect sneaky edits |
— |
| Has test | ✅ | Upgrade: run $TEST_CMD to judge progress |
Upgrade: run test to judge regression | Upgrade: runnable red→green test |
| No git, no test | ✅ full | Downgrade: content-hash snapshot, force a stop after N unchanged rounds | Downgrade: only audit.log trace |
Downgrade: static assertions |
loop-guardis decoupled from git/test: what degrades is only the precision of stall/audit, not the anti-cheat itself.
loops-init/
├── SKILL.md # main skill doc (English; full Step0–6 + §7 boundaries + §8 checkpoint)
├── lib/
│ ├── probe-infra.sh # probe HAS_GIT/HAS_TEST/CAN_COMPILE/OS/SHA_CMD
│ ├── probe-hooks-enabled.sh # probe whether hooks can actually run (fail-loud)
│ ├── render.sh # pure-bash literal substitution of __NAME__ placeholders
│ └── merge-settings.sh # idempotently merge hooks into settings.json (purge-then-add + marker fingerprint)
├── templates/
│ ├── hooks/{loop-guard,loop-stall,post-audit}.tmpl.sh # already debug-hardened; render only substitutes __X__
│ ├── agents/
│ │ ├── builder.tmpl.md # {{...}} filled intelligently per spec
│ │ ├── checker.tmpl.md # tools line has no Write/Edit
│ │ └── progress.tmpl.md # scaffolds the PROGRESS.md ledger
│ └── dod/wave.dod.tmpl.yaml # per-wave DoD template
└── fixtures/
├── canonical.values # fixed neutral values for the Step5 golden comparison
└── hooks.golden/ # golden snapshots (verbatim expected output of render(template, canonical))
Two placeholder systems, don't mix them: hook templates use
__NAME__(render.shmechanical literal substitution); agent / dod / progress templates use{{NAME}}(loops-init fills intelligently by reading the spec + CLAUDE.md, not mechanical substitution).
/loops-init <feature ID or one-line requirement>
The skill runs through Step0–6, writes the files into the project's .claude/, then stops, and prints a delivery report (paths of each spec md + all doubts, the two agent paths + the pending-review list, the PROGRESS ledger, the hooks registration status, archived old agents).
- review the spec's contradiction resolutions / semantic-ambiguity items;
- calibrate the agents' domain sections, removing the
<!-- ⚠️ 草稿·待人审 -->markers; - restart the session (so the new agents + hooks load and take effect).
The skill emits two native /goal driver strings; after restart, pick one:
- Mode A · Wave-by-wave human review (default, recommended): one string per wave; review the previous wave's
PROGRESSbefore pasting the next. Wave boundary = human-review gate + fresh context per wave. - Mode B · One-shot run-to-completion (opt-in): a single composite string; the main
/goalsession advances all waves in order by itself. The cost: losing the inter-wave human-review gate, main-context bloat across waves, blurred stop semantics — for single-wave projects the two modes are equivalent; just use A.
The loop's brake: a wave that hits "the same failure twice (compared by rule-id, not file:line)" or "8 rounds not reached" stops at that wave, and writes the triggering rule-id + the checker's verbatim words into the "repeatedly-failing rule-id" zone of PROGRESS.md, for humans to harden the spec.
- agent_type live-test (the linchpin): before bringing
loop-guardonline, first install a temporary probe that onlyechos and allows everything, and confirm with your own eyes theagent_typevalue the main session sends — if it's"main", tighten to fail-closed; if it's absent, keep it lenient (otherwise you'd lock out the main session / human reviewer). Before the live test, do not go online fail-closed. - Idempotent settings merge:
merge-settings.shuses purge-then-add + a marker fingerprint at the command tail (# loops-init:NAME:vN), keeping only the current version, never overwriting the user's existing permissions / env / other hooks; re-running won't leave v1/v2 coexisting and running deny twice. - Golden comparison guards against backsliding: the hook templates are debug-hardened one by one (no pipefail,
${var}to prevent unbound,[^[:alnum:]_]boundaries, etc.). Step5 uses the golden snapshots to catch "optimizing the template back into the pit". If you change a template you must regeneratefixtures/hooks.golden/withrender.sh, or Step5 will alarm.
- As long as the checker/builder keeps Bash, you cannot 100% prevent "writing files via Bash" (Turing-complete:
python -c open()/ base64 / redirection; regex can't catch them all). loop-guard's Bash interception blocks "casual/lazy weakening of checks", not "deliberate bypass". - builder/checker is a non-adversarial scenario (the agent only cuts corners to fake green, it's not an attacker), so "blocking the casual" suffices — this is an accepted residual, not a blocker. Rooting it out would require shrinking the checker's Bash to a command allowlist; not worth it in a non-adversarial scenario, left as go-forward.
- Wave-level checkpoint (HAS_GIT only): after each
ALL GREENwave and PROGRESS is written, the main session runsgit tag loop-wave<N>-green, recording the tag name in PROGRESS's checkpoint column; failed waves are not tagged. - Rollback is manual:
git reset --hard loop-wave<N>-green(destructive, discards all changes after that point). The rollback target only recognizes waves withstatus=GREEN(with checker evidence) in PROGRESS — even if the builder forges a tag via Bash it's harmless (it can't write PROGRESS). - Auto-rollback is not implemented: this version chooses "stop and hand off to a human + preserve the failure scene for diagnosis".
This skill produces only the project-agnostic layer (scaffold structure, the three guardrails, the 5-category cross-check framework, the hook mechanism, the
/goalshape, the boundaries). Supplied by the project (differs per project): the source-of-truth format and path, tech stack / iron laws (read the projectCLAUDE.md; this skill only reads, never writes), naming, whether it compiles / is testable, the read-only reference directory.
So the upper bound on this skill's output quality = the quality of the target project's CLAUDE.md + source of truth. Garbage in, garbage out.