loop run is the foreground Trigger. It loads loop.config.ts from the current
directory, claims the Lock, and drives Rounds until the Loop settles (the Verify
agent’s Verdict ends it — met or given up) or a guard stops the Run first. The
loop bin is project-local, like next dev — run it from the project directory,
never install it globally.
loop.js runs no daemon. When this process ends, the Run has ended; the next
Trigger resumes from the Record.
Options
-n <rounds> caps this Run only: after <rounds> Rounds it exits yield
(exit code 0) with the Loop still live, ready for the next Trigger. The Loop-wide
limits.rounds guard in the config is untouched and still wins if it fires first.
--fresh ignores any prior Record: the engine claims the Lock, then clears
workspace/, .loop/, and .handoff/, so the Loop starts over at Round 1. A
live owner still refuses it — --fresh never steals a running Loop.
--force takes over the Lock from a live owner. It stops the owning process
first — SIGINT (the clean cancel path, which flips the Record to stopped), then
SIGKILL after a 10-second grace if it will not stop — and claims the Lock as a
takeover. A stale claim needs no stopping; the takeover claim reaps it.
Exit codes
The Run’s terminal Exit maps to the process exit code, so a wrapper can branch without parsing output:| Code | Meaning |
|---|---|
0 | The Run ended the way it was asked to: the Verdict was met, or an -n slice ended in yield (Loop still live) |
1 | Error — including startup failures: no config, a malformed one, or a live owner holding the Lock |
2 | Gave up — settled on a Verdict the Loop could not meet (impossible) |
3 | The budget guard fired |
4 | The limits.rounds guard fired |
130 | Cancelled — Ctrl+C (128 + SIGINT) |
cancel cause: the engine stops cleanly and the Record is flipped
to stopped, so the next Trigger resumes where this Run left off.