Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

loop-engine

A two-agent generator–reviewer build loop, packaged as a Claude Code skill (/loop-engine). One orchestrator drives two disjoint subagents — a generator that builds a software target and a reviewer that grades it — and iterates until the result converges to a quantitative quality bar. The bar, the rubric, and how each criterion is graded all come from a frozen, pre-authored domain contract selected as a subskill. The orchestrator decides continue / restart / stop; it never writes the target code or grades it.

The idea

An agent build loop needs three things settled before it can converge: what to build, what "good" means, and when to stop. loop-engine fixes the last two up front by making the rubric a first-class, reusable artifact — a loop contract — instead of something a planner agent re-derives every run. A contract carries:

  • a set of criteria, each scored 0–10 with calibrated anchor_0 / anchor_10;
  • a grading mechanism per criterioncode-read, cli-exec, http-probe, screenshot, playwright, and so on — so the reviewer proves each score the way the domain demands;
  • the stopping bar (stopping_target_fitness, asymptote_band).

Because the contract is authored and validated before the run and frozen into the workspace, every run of a given domain is graded against the identical bar, and the loop has only two build-time roles instead of the usual planner + generator + evaluator.

The roles (disjoint)

Role Does Never
Orchestrator resolves/authors the contract; freezes it; decides continue/restart/stop; manages the restart handoff codes; grades; edits the frozen contract
Generator builds everything to maximize fitness against the contract grades its own work
Reviewer grades 0–10 using each criterion's declared mechanism; logs the vector + fitness; classifies the trajectory; signals restart writes target code; changes the contract

Keeping build and grade apart is the point: the moment one agent does both, it turns sycophantic and the loop converges on slop.

The fitness function

Every criterion is scored on an empirical 0–10 scale. For a result with N criteria scored X = (x₁, …, x_N):

fitness(X) = √(Σ xᵢ²) / (10·√N) = √( (1/N) · Σ (xᵢ/10)² )

This is the L2 norm of the score vector normalized by the all-tens vector — equivalently the root-mean-square of the normalized scores. It is 1.0 when every criterion is a 10 and 0.0 when all are 0. RMS deliberately rewards peak excellence over uniform mediocrity in early cycles, while the high convergence target (default 0.95) still forces near-perfect scores across essentially every criterion. A per-category floor (category_floor, default 3) keeps any category from being neglected. The math is implemented and enforced in scripts/fitness.py — compute with its CLI, never by hand.

The loop

A session is a sequence of loops; a loop is one initialization plus its cycles; a cycle is one build → grade → classify iteration. There is no plan step and no negotiation step — the contract is already the specification.

resolve + validate contract → freeze into .loop-engine/<slug>/
  └─ per loop:  ┌─ generator (build) → reviewer (grade per mechanism) → classify ─┐
                └──────────────── orchestrator: continue / restart / stop ─────────┘

The reviewer computes fitness and classifies the run's fitness trajectory, which drives the restart decision:

Trajectory Action Meaning
converged stop at/above target, or a settled in-band plateau
converging / plateau continue still climbing or with headroom
cyclic / diverging / strange / stuck_low restart this loop's inputs can't reach target

Because the contract is frozen, a restart changes only the enriched logs and small generator-prompt tweaks — never the rubric. If the loop reveals the contract itself is wrong (an unsatisfiable criterion, a coverage gap, a mechanism the environment can't run), that is a human escalation: fix the contract subskill and start a new run.

Invocation

/loop-engine --<domain>[-loop-contract] [target…]   run the loop using that contract
/loop-engine --<domain> --<domain2> [...] [target…]  multi-contract run: merge those contracts, run one loop
/loop-engine --new-contract <specification>          author a NEW contract, validate, register
/loop-engine --list                                  list registered contracts and multicontracts
/loop-engine <freeform target description>           auto-select a contract; ask if ambiguous
/loop-engine            (bare) | --help               help screen

--supervised composes with any run form (human gate after every cycle).

Contracts (the subskills)

Each contract is a directory contracts/<domain>-loop-contract/ holding a contract.json. Eight ship out of the box:

Contract Grades by Weighted toward
web-spa-loop-contract playwright + code-read functionality / design
cli-tool-loop-contract cli-exec + code-read + unit-test functionality / robustness
rest-api-loop-contract http-probe + unit-test + code-read functionality / scalability
static-site-loop-contract screenshot + playwright + code-read design / craft
seo-geo-loop-contract http-probe + code-read + playwright machine-access / content
software-design-loop-contract code-read + unit-test + static-analysis correctness / system-fit
website-marketing-optimization-loop-contract playwright + code-read + screenshot + http-probe messaging / funnel
website-design-optimization-loop-contract screenshot + playwright + code-read typography / space-layout / mobile-adaptive / accessibility

Add your own with /loop-engine --new-contract <spec>. Every contract — shipped or authored — must satisfy the canonical template contracts/CONTRACT.schema.json, enforced by scripts/contracts.py. A contract that does not validate cannot start a run.

Multi-contract runs & multicontracts

Any set of registered contracts can be optimized together in one run: stack their flags (/loop-engine --website-design-optimization --seo-geo <target>) and the orchestrator merges their rubrics into ONE schema-valid contract — criterion ids and categories namespaced by member domain, priorities round-robin interleaved so all members improve together, the strictest stopping bar kept — validates it, and freezes it like any other contract. Because the merged artifact is itself a valid contract, the loop downstream is byte-for-byte a normal run. The merge is deterministic and lives in the same enforcer:

python scripts/contracts.py merge <name-or-domain-or-path>... [--name X] [--out FILE]

A multicontract is a registered preset combination — a directory contracts/<name>-multicontract/ holding a multicontract.json that names ≥ 2 member contracts (template: contracts/MULTICONTRACT.schema.json). It dispatches exactly like any contract subskill and expands to its members at merge time. One ships out of the box:

Multicontract Dispatch Members
website-optimization-multicontract --website-optimization website-marketing-optimization + website-design-optimization + seo-geo

--website-optimization drives a website to best-in-class on marketing/conversion, visual and interaction design, and SEO/GEO simultaneously. Full merge rules, the orchestrator's semantic-dedupe pass, and preset authoring: references/multi-contract.md.

Layout

loop-engine/
├── SKILL.md                          orchestrator + dispatcher (the skill entry point)
├── README.md                         this file
├── pyproject.toml                    package metadata (standard library only; no runtime deps)
├── references/
│   ├── fitness-and-convergence.md    fitness formula, convergence/stopping, trajectory heuristics
│   ├── roles-and-restart.md          two-role contract, mechanism-directed grading, restart under a frozen contract
│   ├── state-and-run.md              workspace + state schema, dispatch grammar, contract resolution, intake
│   ├── multi-contract.md             multi-contract runs: merge rules, semantic dedupe, preset multicontracts
│   ├── contract-authoring.md         how --new-contract writes a template-compliant contract (+ multicontracts)
│   └── prompts/                      generator / reviewer system-prompt templates
├── scripts/
│   ├── fitness.py                    deterministic fitness + trajectory classifier (+ CLI)
│   ├── contracts.py                  load / list / validate / merge contracts + multicontracts (the template enforcer)
│   └── test_fitness.py, test_contracts.py   pytest suites
├── contracts/
│   ├── CONTRACT.schema.json          the canonical template every contract must satisfy
│   ├── MULTICONTRACT.schema.json     the template every preset multicontract must satisfy
│   ├── <domain>-loop-contract/contract.json   the shipped domain contracts (subskills)
│   └── <name>-multicontract/multicontract.json   the shipped preset combinations
└── assets/templates/                 fill-in run-state files (progress, log, evals, restart-brief)

Install as a Claude Code skill

The repository is the skill (SKILL.md lives at its root). Make it available as /loop-engine by linking — or copying — it into the skills/ folder of your Claude Code configuration directory (the dot-prefixed claude folder in your home directory, or $CLAUDE_CONFIG_DIR where set), under the name loop-engine:

# run from inside the repo; <config-dir> is your Claude Code configuration directory
ln -s "$(pwd)" <config-dir>/skills/loop-engine
# or copy it:  cp -r . <config-dir>/skills/loop-engine

Paths inside SKILL.md (e.g. scripts/fitness.py) are relative to the skill directory, so linking the repo root is all that's needed. Then invoke /loop-engine and either name a contract (--web-spa) or describe the target and let it pick.

For agents

If you're an AI agent that just read this repo online, here's the whole path from zero to a running loop.

1 — Load it as a skill. loop-engine is a Claude Code skill, not a library: you invoke it with /loop-engine, you don't import it. Link the repo into the skills directory (see Install above for the location and the copy alternative):

ln -s "$(pwd)" <config-dir>/skills/loop-engine   # from the repo root

2 — Verify it's healthy (pure standard library, no install step):

python -m pytest                     # suites green
python scripts/contracts.py list     # shipped contracts, all "valid": true

3 — Use it properly.

  • Choose the bar first. Name a contract by domain (/loop-engine --rest-api <target>), stack several to optimize them together (--website-design-optimization --seo-geo), use a preset multicontract (--website-optimization), let it auto-select from a freeform description, or author a domain with /loop-engine --new-contract <spec>. --list shows what's available.
  • Let the orchestrator run the loop. Don't act as the generator or reviewer yourself — the design depends on those being separate agents. You pick the target and contract; the skill drives build → grade → classify → continue/restart/stop.
  • The environment must support the contract's harness. A playwright contract needs a browser; http-probe / unit-test need the server or suite to come up. A mechanism that can't run is a sufficiency-gate failure to surface, not something to grade around.
  • Contracts freeze at run start. If one turns out wrong (an unsatisfiable criterion, a coverage gap), fix the contract subskill and start a new run — never hand-edit a frozen contract mid-loop.

Develop

fitness.py and contracts.py are pure standard library — no third-party dependencies. Run the suites:

python -m pytest

Use the CLIs directly:

python scripts/fitness.py score --vector 8,9,7,10
python scripts/fitness.py classify --evals path/to/evals.jsonl --target 0.95
python scripts/contracts.py validate contracts/web-spa-loop-contract/contract.json
python scripts/contracts.py list
python scripts/contracts.py merge website-optimization --out merged.json   # preset expands to its members
python scripts/contracts.py validate merged.json

Lineage

Implements the "write the loop, not the prompt" pattern — generator/reviewer separation, state on disk, a deletable harness, a quantitative fitness function, and a trajectory-based restart rule. Its one distinguishing move is replacing the run-time planner and rubric negotiation with a frozen, validated contract that also declares how it is graded, so the rubric becomes a reusable, testable artifact rather than something re-derived each run.

About

Generator–reviewer build loop as a Claude Code skill: iterate a build to a frozen, self-grading domain contract until it converges to a quantitative fitness bar.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages