Getting Started
loopexec is a deterministic runtime for loop engineering: it runs bounded, auditable work loops that stop on a computed condition, never because an agent reports it is done. This page installs the binary, states precisely what ships today, and points to where the engine is going.
Install
Section titled “Install”loopexec is a single Go binary. You need Go 1.26+ on your PATH.
Build from source
Section titled “Build from source”git clone https://github.com/justyn-clark/loopexec.gitcd loopexecgo build -o loopexec ./cmd/loopexecInstall with go install
Section titled “Install with go install”go install github.com/justyn-clark/loopexec/cmd/loopexec@latestVerify the install:
loopexec --helploopexec - deterministic runtime for loop engineering
Available Commands: check Validate loop invariants (state hygiene, not the application oracle) init Initialize loopexec workspace metadata run Run a bounded check_fixpoint loop until the check passes or a bound trips status Show loop status step Execute a single loop stepYour first loop (60 seconds)
Section titled “Your first loop (60 seconds)”A loop needs two things: a work step (--exec) and an external check (--check) that decides when the work is done. In this toy, the check passes once a file says green and the work step flips it - the smallest honest loop, no agent required.
mkdir loop-demo && cd loop-demoecho red > status
loopexec run --run-id demo \ --exec 'echo green > status' \ --check 'grep -q green status' \ --max-iterations 5run does the work step, runs the check, and stops the instant the check passes - on a computed halt, never on a claim:
loopexec 0.2.0status: haltedrun_id: demoiteration: 1halt_reason: success_condition_metreceipt: .loopexec/run-demo.jsonlExit code 10 is the converged class. A converged run is a success, so it prints nothing to stderr; CI can branch on the code without parsing JSON.
It left a receipt
Section titled “It left a receipt”Every iteration appended a typed JSONL event - the work step, the check and its exit code, and the computed halt:
$ cat .loopexec/run-demo.jsonl{"ts":...,"run_id":"demo","iteration":0,"event":"run_start","detail":"check=... exec=... max=5"}{"ts":...,"run_id":"demo","iteration":1,"event":"iter_start"}{"ts":...,"run_id":"demo","iteration":1,"event":"exec","exit_code":0}{"ts":...,"run_id":"demo","iteration":1,"event":"check","exit_code":0}{"ts":...,"run_id":"demo","iteration":1,"event":"halt","detail":"success_condition_met"}Or read it as a digest - outcome, pins, and the iteration timeline - with loopexec report --run-id demo:
report: run "demo"phase: halted (success_condition_met, exit 10)iterations: 1check: grep -q green statusfingerprint: exit 0, sha256 e3b0c44298fcreceipt: .loopexec/run-demo.jsonl (5 events)
timeline: [iter 1] exec exit 0 [iter 1] check exit 0 [iter 1] halt success_condition_metVerify the verdict offline
Section titled “Verify the verdict offline”replay re-runs only the check against the recorded fingerprint - agent-free, budget-free - and confirms the receipt still holds:
loopexec replay --run-id demo --json{"tool":"loopexec","version":"0.2.0","status":"verified","run_id":"demo","verified":true,"errors":[]}And explain-halt says what to do about the halt - here, nothing, it converged:
loopexec explain-halt --run-id demohalt_reason: success_condition_metverdict: donewhy: the external check passed; the loop converged.Before you trust a check overnight, measure it: loopexec probe-check --check 'go test ./...' reports a 95% confidence bound on its flake rate (no check, no loop). The full unattended-loop discipline is in Safe Overnight Loop.
What ships today
Section titled “What ships today”The following are Shipped in cmd/loopexec with tests, and conform to the canonical command contract:
- The CLI contract.
--jsonprints exactly one JSON object to stdout. Human logs and errors go to stderr. Output is deterministic and the exit codes are stable. init/status/check/stepcommands. Workspace scaffolding, status read-out, state-hygiene validation, and a single-step debug surface.runas the real iterating loop. Each iterationrunexecutes the work step (--exec), runs the external check (--check) once, evaluates guards before any green branch, applies the anti-regression ratchet, computes its halt from observed state, and writes a typed JSONL receipt plus durable, resumable state (section 4). The stable--jsonenvelope and exit-code mapping hold throughout.
The section 4 iterating engine ships: single check capture per iteration (O6), the anti-regression ratchet, progress and feasibility halts, computed halt reasons, determinism held as a maintained confidence bound (the probe-check bound), and the typed receipt with durable state. replay/reexecute and attest/ack ship in full; the metric-integrity gate, the doctor precondition gate, ratchet, build-context, escalate/watch, and two-zone isolation ship at their core, with named sub-parts (live cost metering, doctor hermeticity and the coverage-delta tier of adequacy, git revert-to-best, build-context import_closure/dep_graph tiers, the container/egress/key hooks) still Planned. Every command ships; what remains Planned is named inline sub-parts of the shipped cores (live/auto cost metering and in-loop budget enforcement, doctor hermeticity and the coverage-delta tier of adequacy, the deeper integrity layers, and the operator-provided isolation hooks). For the exact split, read Capabilities, which mirrors SPEC section 11.
First run
Section titled “First run”Scaffold a workspace. init ships today:
loopexec init --json{"tool":"loopexec","version":"0.2.0","status":"initialized","errors":[]}Read state back at any time. status and check ship today; check validates state hygiene and is explicitly not the application oracle:
loopexec status --jsonloopexec check --jsonThe loop itself is run. The check it loops against is external by contract - an independent process whose exit code is the oracle. An agent’s self-assessment is not a check, so run will not invent one. The shapes below are what run emits; the section 4 engine that produces them from a live loop ships.
Without --check, the contract requires run to refuse to start and halt workspace_invalid (exit 30):
loopexec run --json{"tool":"loopexec","version":"0.2.0","status":"error","run_id":"local","halt_reason":"workspace_invalid","errors":["a loop requires an external check (--check). no check, no loop"]}Point --check at your real test, build, lint, or score command - its success_exit_code is the stop condition:
loopexec run --check "go test ./..." --max-iterations 10 --jsonOn convergence, run is specified to halt success_condition_met and exit 10:
{"tool":"loopexec","version":"0.2.0","status":"halted","run_id":"local","iteration":1,"halt_reason":"success_condition_met","check_exit":0,"receipt":".loopexec/run-local.jsonl","errors":[]}A run that spends its iteration fuse halts max_iterations_reached and exits 12.
The machine contract
Section titled “The machine contract”Two things are stable and meant to be branched on by CI and integrations:
- The
halt_reasonstring is the precise integration contract - every reason is computed from observed state, never set by a flag. - The exit code is a coarse class CI can branch on without parsing JSON.
| exit | class | example halt_reason |
|---|---|---|
0 | nominal | (loop ran, no halt) |
10 | converged | success_condition_met |
11 | terminal-blocked | no_actionable_tasks, human_required |
12 | iteration-cap | max_iterations_reached |
20 | invariant | invariant_failed |
30 | workspace | workspace_invalid |
40 | execution | execution_failure |
50 | internal | internal_error |
Classes 13 (integrity), 14 (oracle-trust), 15 (check-inadequate, via doctor --mutate-cmd), 16 (resumable-judgment), 17 (no-convergence), 18 (budget, via inspect-cost), and 19 (liveness/drift) all emit today; only class 11’s task-list reasons stay reserved. The full map lives in the CLI Reference and is explained in Halt Reasons.
Every --json object carries at least {tool, version, status, errors[]}, plus {run_id, iteration, halt_reason} where applicable. The schema is additive: new fields may appear; existing field meanings do not change within a major spec version.
SMALL is optional
Section titled “SMALL is optional”loopexec runs standalone by default. It composes with SMALL-governed repositories - where the loop iterates a plan and gates on state hygiene plus a per-task acceptance oracle - but it does not require SMALL to run. check validates state hygiene; it is explicitly not the application oracle, because state hygiene cannot decide application correctness.
Where this is going
Section titled “Where this is going”loopexec is specified to be the runtime that makes a loop stop, explain why with a computed halt reason, and leave a receipt you can verify offline without re-running the agent - replayable verdicts, not replayable runs. The verdict replays; a live-LLM trajectory does not.
- Capabilities - the Shipped / Shipped (core) / Planned matrix, straight from SPEC section 11. Start here to know exactly what is real.
- Loop Engineering - the discipline: external oracle over self-grading, stateless iteration, guards dominate success, and computed halts.
- Determinism - why a check is treated as a maintained confidence bound, not a one-time probe.
- CLI Reference - the full command surface, flags, exit codes, and JSON schema.
Maintained by Justyn Clark Network