Skip to content

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.

loopexec is a single Go binary. You need Go 1.26+ on your PATH.

Terminal window
git clone https://github.com/justyn-clark/loopexec.git
cd loopexec
go build -o loopexec ./cmd/loopexec
Terminal window
go install github.com/justyn-clark/loopexec/cmd/loopexec@latest

Verify the install:

Terminal window
loopexec --help
loopexec - 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 step

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.

Terminal window
mkdir loop-demo && cd loop-demo
echo red > status
loopexec run --run-id demo \
--exec 'echo green > status' \
--check 'grep -q green status' \
--max-iterations 5

run 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.0
status: halted
run_id: demo
iteration: 1
halt_reason: success_condition_met
receipt: .loopexec/run-demo.jsonl

Exit 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.

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: 1
check: grep -q green status
fingerprint: exit 0, sha256 e3b0c44298fc
receipt: .loopexec/run-demo.jsonl (5 events)
timeline:
[iter 1] exec exit 0
[iter 1] check exit 0
[iter 1] halt success_condition_met

replay re-runs only the check against the recorded fingerprint - agent-free, budget-free - and confirms the receipt still holds:

Terminal window
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:

Terminal window
loopexec explain-halt --run-id demo
halt_reason: success_condition_met
verdict: done
why: 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.

The following are Shipped in cmd/loopexec with tests, and conform to the canonical command contract:

  • The CLI contract. --json prints 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 / step commands. Workspace scaffolding, status read-out, state-hygiene validation, and a single-step debug surface.
  • run as the real iterating loop. Each iteration run executes 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 --json envelope 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.

Scaffold a workspace. init ships today:

Terminal window
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:

Terminal window
loopexec status --json
loopexec check --json

The 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):

Terminal window
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:

Terminal window
loopexec run --check "go test ./..." --max-iterations 10 --json

On 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.

Two things are stable and meant to be branched on by CI and integrations:

  • The halt_reason string 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.
exitclassexample halt_reason
0nominal(loop ran, no halt)
10convergedsuccess_condition_met
11terminal-blockedno_actionable_tasks, human_required
12iteration-capmax_iterations_reached
20invariantinvariant_failed
30workspaceworkspace_invalid
40executionexecution_failure
50internalinternal_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.

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.

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