Authoring a loop you can trust
A .loop.yaml that parses isn't necessarily one worth running.
This guide covers how to author a spec that's runnable and
trustworthy — one whose checks actually test the requirement and
start RED before the agent begins. (If you use Claude Code, the in-repo
author-loop skill runs these steps for you.)
loopgen generate already turns flags into a valid skeleton.
What it can't supply is the judgment: reading the actual target repo to
pick the right checks, writing concrete requirements,
choosing the trust policy, and proving the spec before
any agent budget is spent.
The one rule that matters most
When a loop fits (and when it doesn't)
A loop is only as good as the checks you hand it. loop-generator shines where success is mechanically checkable and misleads where it isn't, so it's worth knowing which side of that line a task falls on before you spend agent budget.
Works best when
- The run is unattended — overnight, in CI, or a fleet of agents — and you'd rather verify "done" than take the agent's word for it.
- The task is well-scoped and the repo already has (or you can add) real test/metric infrastructure.
- There's a specific suite to make green while keeping the rest green — e.g. a failing test that encodes the new behavior and turns green only when it's done.
- The target is a measurable number: keep p95 under X ms, coverage
≥ 90%, bundle under Y KB. (The
experimentevaluator is built for this.) - You're remediating against a concrete check across many call sites — add retries/logging/validation, fix a lint rule repo-wide, drag a flaky suite to green.
Poor fit
Tasks where the framework mostly ends up telling you the checks were the wrong contract:
- "Improve the architecture," "make the UI delightful," "clean this up" — no exit-zero definition of done.
- Subjective or judgment-heavy work (API ergonomics, copy, visual polish): a passing check doesn't mean the outcome is good.
- Large, novel refactors with emergent design — the loop can't hold the whole design in mind across iterations, and the checks rarely capture "is this the right structure."
What it can't solve
These are fundamental to the agentic-loop approach, not gaps to be closed. The trust guards make them visible; they don't eliminate them:
- Mis-specified or insufficient checks. The checks are the contract — bad checks, bad contract. A green run only ever means "the checks passed."
- Agents that game metrics or ship minimal patches. An agent can satisfy a surface check without doing the real work: hard-code the expected value, weaken an assertion, ship the narrowest patch that passes.
- Context degradation over many iterations. The agent re-derives state and drifts as the loop grows; feeding the last diff back into the next prompt mitigates this — it doesn't cure it.
- The underlying unreliability of LLM agents on large, novel work. More iterations don't turn an unreliable agent into a reliable one on a task it can't hold in its head.
The skill pipeline: let an agent run this workflow
The engine audits the run; these skills produce checks worth auditing.
The repo ships Claude Code skills (.claude/skills/) that
load automatically when you open it in Claude Code — three of them form
the authoring pipeline, from a whole app spec down to a runnable loop:
frame-app ─────► frame-checks ────► author-loop ──► loopgen run
whole app into one request into one verified (debug-loop when a
RED-able slices falsifiable .loop.yaml, run goes sideways)
(a .batch.yaml) acceptance checks proven RED
frame-app— decompose a whole app spec into a dependency-ordered DAG of RED-able vertical slices (a "loop of loops"), feeding each buildable frontier toframe-checksand promoting verified loops into a.batch.yaml.frame-checks— turn one request into falsifiable acceptance checks: each RED now, for the right reason, and hard to fake.author-loop— run the interview → inspect → decide → generate → prove steps on this page, ending in a lint-clean spec whose checks start RED.debug-loop— diagnose a failed, stalled, or suspiciously-green run by itsoutcome, reproducing the failing check without spending agent budget (the debugging guide).add-driver— scaffold a new agent backend and drive it to green againstloopgen verify-driver(extending).
No Claude Code? The pipeline is just this page plus debugging — the skills automate the workflow, they don't replace it.
1. Pin down the goal
Write the goal as something a command can verify. Push vague asks until they're testable:
- "Make search better" is unusable.
- "Add case-insensitive substring matching to
searchUsers()so/users?q=ALmatchesalice" is a loop.
You also need three more things:
- Where the work happens — the target repo, which
becomes
workspace.dir. Get an absolute path, or confirm the path relative to where the spec file will live. - What "done" looks like — the signal that proves success: a failing test that should pass, a metric that should move, a build that should stay green.
- Which driver —
claude-agent-sdk(default),grok,github-copilot,opencode, ormock(the offline demo). Real hosted drivers need credentials.opencodeis the local-inference path: pointdriver.options.modelat a tool-calling LM Studio or Ollama id inprovider/modelform (e.g.lmstudio/qwen/qwen3-coder-next). See Local models.
If the goal is too vague to write a check for, resolve that now. A loop with no real check is the most common way these waste hours.
2. Inspect the target repo — don't trust the defaults
This is the step generation can't do for you. Open the workspace and find ground truth:
- Language / package manager / framework — from
package.json,pyproject.toml,go.mod,Cargo.toml,Gemfile,pom.xml. - The REAL commands. The generic scaffold guesses
(
npm test,npx tsc --noEmit,pytest -q, and so on). Replace them with what the repo actually uses — read thepackage.jsonscripts, the Makefile, the CI config. A wrong test command is a guaranteed failed run. - The thing being changed — find the file, function, or endpoint named in the goal so the requirements can reference real symbols and paths.
- Is it a git repo? Change detection and snapshots rely
on git; a non-repo workspace trips the
SPEC-WORKDIR-NOT-PROJECTlint rule. - State and side effects — does the test command touch
a database or a dev server? That decides
evaluation.concurrencyand whether a strict baseline is safe to run twice.
3. Decide the spec (with reasons)
task.type—function/api/webapp/experiment/generic. Picks the prompt framing and the checks the scaffold suggests.evaluators— the heart of the spec. Include at least one check that fails until the requirement is met (usually a test asserting the new behavior), and use the repo's real commands. For a numeric target, use theexperimentevaluator. Avoid smoke tests that create their own data but never drive the real entrypoint — they can pass without exercising the feature.success—all-passis the common case; usescore/pass/all/anyfor thresholds or partial gates.limits.baseline— default to"strict"so an already-green (vacuous) check set fails fast withbaseline-vacuous. Drop tofalseonly when the checks side-effect and can't be run twice.limits.specGuard— defaults toerror: a mid-run edit of the spec fails the run asspec-tampered. Keep the spec outsideworkspace.dirso the agent never has a reason to touch it; leave the default alone either way.limits.evaluatorGuard— defaults toerror: the test files acommandcheck runs are the real success criteria, and a mid-run edit of them fails the run asevaluator-tampered. Addguard:paths on any check whose grader isn't named in its command (a scorer, labeled data). Drop towarnonly for a check that legitimately rewrites its own fixtures.limits.maxIterations— a budget, not a target. 5–8 is typical; small, well-checked tasks need fewer.
4. Generate the skeleton, then edit
Generate a guaranteed-valid skeleton, then fix the fields the defaults got wrong. Editing a generated file beats hand-writing YAML — the skeleton always parses, so you only change values.
loopgen generate \
-n "add-retry-to-fetchUser" \
-t function -l typescript -f express \
-d claude-agent-sdk \
-r "Add exponential backoff (max 3 retries) to fetchUser(); keep the signature." \
-m 6 -o ./loops/add-retry.loop.yaml
(From the repo without a build, prefix with npm run loopgen --,
e.g. npm run loopgen -- generate ….)
-d opencode seeds
dangerouslySkipPermissions: true; interactive
(-i) also asks for a provider/model id
(e.g. lmstudio/…).
Then edit the file to: swap in the real test/build
commands, set workspace.dir to the target repo, set
limits.baseline: strict, tighten requirements,
and adjust success if needed. Keep the spec
outside the target repo and point
workspace.dir at it.
5. Prove it — the part that earns trust
Two gates, neither of which spends agent budget.
a. Lint clean
loopgen lint ./loops/add-retry.loop.yaml --strict
Resolve every ✗ error and ⚠ warning. Don't hand
off a spec with open errors. Each rule and its fix is documented in
the lint reference.
b. The checks start RED for the right reason
There is no agent-free "baseline only" run mode, so verify directly: run each evaluator's command yourself in the target workspace and confirm it fails because the requirement isn't met yet — not because a binary is missing, the working directory is wrong, or the command is bogus.
# in the target repo, on an untouched checkout:
npm test # should FAIL (the new behavior isn't implemented yet)
npx tsc --noEmit # should pass or fail per the real baseline
If a check passes on the untouched workspace, it doesn't test the
requirement — fix the check before continuing. This is the same contract
limits.baseline: strict enforces at runtime; you're
confirming it up front so a run doesn't waste budget discovering it.
6. Run it
Once lint is clean and the checks start RED, you're ready:
loopgen run ./loops/add-retry.loop.yaml --strict-baseline --report run.json
A real run spends agent budget and needs the driver's credentials
(ANTHROPIC_API_KEY for claude-agent-sdk,
XAI_API_KEY for grok, and so on). The
--report run.json file is the richest input to the
debugging workflow if the run doesn't go
green.
Guardrails
- Don't fabricate the test command. Read it from the repo. If there isn't one, say so and consider adding a test as part of the loop.
- Don't ship a vacuous loop. If you can't make a check that's RED before the work, the loop isn't ready — that's a signal the task may be a poor fit, not a spec to force through.
- Keep the spec out of the target repo unless you have
a reason not to; if it lives inside, set
specGuard: error. - Re-lint after every edit. It takes milliseconds and catches compounded relative paths and missing binaries before a run does.
Reference
For exact field names, defaults, the per-language default commands,
evaluator options, success-criteria forms, and the full lint-rule → fix
table, see the in-repo author-loop skill's
reference.md
(.claude/skills/author-loop/reference.md) and the
spec section. The source of truth
is the code: src/core/spec.ts (schema),
src/tasks/ (task types and default commands), and
src/lint/rules.ts (lint rules).