A "terraloop" sounds like a framework. It isn't. It's a name for a shape three things fall into: a heartbeat that keeps re-prompting the agent, the agent that holds the state and spawns work into terrarium, and terrarium handing back a receipt so the agent knows what actually happened.
The part people skip is the state. The heartbeat only re-runs the prompt; what makes the loop actually move is that the agent carries its progress from one tick to the next, and the receipts from terrarium are what it trusts to update that state. Lose the state and you just have a cron job re-reading the same instruction forever.
A command like /loop every 30s check MR !38955; stop this loop once it merges kicks one off. The heartbeat keeps checking; the moment the thing is true, the loop deletes itself. You close the laptop.
Update: The Protocol Is Now a Gate
The first version of this post was a skill: prose the agent could follow or skip. It skipped it. It did the work inline, never locked a contract, and reported the work was done.
terraloop-mode moves the protocol into Pi's tool_call hook. A skipped step is now a blocked tool call with a reason the model cannot argue with.
- Arm.
/terraloopor the agent'saction=arm. Only the user can leave, with/terraloop-off. - Lock. Goal, gate, scope, and proof, after the user says go. No child spawns before this.
- Drive. A
loops_taskdriver must exist. Writes outside the locked scope are blocked, including/tmp. - Gate.
action=gateruns the proof command itself. A non-zero exit refuses the gate, so the agent cannot certify its own work.
Two things changed the heartbeat. A settle tick re-delivers the driver prompt as soon as a working turn ends, so the loop no longer waits out the interval. And an advisory evidence screen asks Clef whether the proof output, with its own PASS labels stripped, shows the gate holds.
The repo also ships loop skills with runnable gates: one for GitLab review queues that rediscovers the queue on every run, one for GitHub issue-to-PR work, and one that reviews the last loop.
Install: pi install git:github.com/acoyfellow/terraloop-mode@main. 76 tests pass. It is not a security boundary: an agent with a shell can edit its own state file.
Tool One: The Heartbeat
loops-yaml is the heartbeat. A loop is a schedule plus a command, nothing fancier. As a Pi/OpenCode extension it re-injects a prompt on an interval while the session is open and idle, so nobody has to sit there re-typing it. The agent runs these through a loops_task tool and deletes the task when it's done.
The heartbeat waits on completion callbacks instead of sleeping or polling. Each tick the agent reaps whatever finished, folds it into its state, moves one step, and kicks off the next.
Tool Two: The Runner
terrarium runs each bounded task as one isolated job and hands back a receipt. This is the part I care about most, because exit 0 lies. A callback lies. Model prose that says "done!" lies.
A real success is a TERRARIUM_RESULT whose server-minted runId, taskFingerprint, and nonce all line up with the task that was handed out. So the agent re-reads the receipt, re-runs the test, or curls the endpoint before it trusts the result into its state. A callback means the run finished, not that it worked.
The Four Habits That Keep It Safe
GOAL IS BINARY
Decide what "done" means before you start. It has to be checkable, not a feeling.
PARALLEL FIRST
Ask what can run at the same time before running one thing. Independent work goes out concurrently.
RIDE CALLBACKS
Advance when a child reports back. Don't sit in a sleep loop burning the session.
DELETE ON STOP
When the goal is true, kill the loop. A loop spinning against a satisfied gate is just noise.
The Whole Thing Is One File
Here's the part you actually copy. One loop is one markdown file. Fill the angle brackets and point your heartbeat at it.
# Terraloop: <one-line goal> Follow the terraloop protocol. This file is the contract for one loop. ## Goal (binary) <the end state, checkable — not a vibe>. DONE = <the exact condition>. ## Method (parallel-first) - Name the smallest steps; run independent ones concurrently. - The heartbeat (loops-yaml) re-injects this prompt each tick while idle. - Each tick: reap finished children, verify, advance ONE step, spawn the next. ## Children (terrarium) - Spawn one bounded child per deep task with: one objective, its PASS criteria, the safety rules. Read-only digs stay read-only; only the gate step deploys. - A child's "done" is a claim. Re-check every receipt: runId + taskFingerprint + nonce must correlate. A callback means finished, not succeeded. ## Stop gate <the binary condition>. Delete the heartbeat loop the moment it is met.
The loop ends the same way every time: the agent verifies a receipt from terrarium, confirms the final state clears the stop gate, and deletes its own loops-yaml task. No dashboard, no daemon left running. Just a file that did its job and a receipt you can still read tomorrow.