Skip to content

State on Disk

State lives on disk, not in chat.

This is the load-bearing idea behind loop engineering. An agent’s memory of what it did is an echo. The context window rots: earlier turns get summarized, evicted, or contradicted, and cost grows roughly quadratically with conversation length. A loop that carries its progress in the window inherits all of that decay.

By contract, each loopexec iteration runs stateless: it rebuilds a narrow context slice from disk, executes the agent, runs the check once (O6, single capture), records the outcome, and discards the window. Progress is durable because it lives in git and in two state files - never in the model’s recollection. Read state from disk and the CLI; never assume chat memory.

The real iterating run loop ships as of v0.2.0 (SPEC section 11): each iteration runs your exec then your check, computes its halt reason, and writes a typed JSONL receipt plus durable state. This page describes that durable-state contract - the behavior a run records today, not a future plan.

Durable state splits into two files with two audiences. One is for the human at 3am. One is for the runtime.

FileAudienceContentsAuthority
STATUS.mdhumanPlain-language narration: what the loop is doing, where it stands, what it is blocked onadvisory
.loop_state.jsonmachineTyped, schema-versioned state the runtime reads to resume and to compute the next halt reasonnormative

The machine file is the source of truth. STATUS.md is a rendering for people. They must agree, but only .loop_state.json is read back into a decision.

The contract (SPEC section 8) requires .loop_state.json to carry at least the following fields. These are the anchors that make a loop resumable and a halt explainable.

{
"schema_version": 1,
"phase": "iterating",
"iteration": 7,
"last_green_commit": "a1b2c3d",
"open_failures": ["pkg/auth: TestExpiry", "pkg/auth: TestRefresh"],
"cumulative_usd": 4.12,
"baseline": { "model_calls": 1, "tokens": 8400, "top_error_type": "assertion" },
"determinism_probe": { "runs": 17900, "stable": true, "confidence": 0.99, "checked_at": "2026-06-25T11:02:00Z" },
"metric_integrity": { "check_cmd_sha": "9f2...", "test_tree_sha_at_last_green": "c4d..." },
"model_pin": "anthropic/claude-opus-4@build-...",
"escalation": { "state": "none", "channel": null, "ref": null, "acked_by": null }
}

open_failures is the failing-test set F_i - the convergence signal, not a grepped line. last_green_commit is the point a regression reverts to. cumulative_usd is the run-total spend ledger. baseline is the Step-1 measurement an explain-halt reads to tell “still improving, raise the limit” apart from “stalled, never retry.”

A receipt is the per-run audit record. The contract is blunt: a receipt MUST pin everything that determines the output, or fail closed with workspace_invalid. A receipt you cannot reconstruct the inputs from is not a receipt.

What must be pinned:

  • Model-identity tuple - {provider, model_id, version/build, quantization, runtime, endpoint}. A registry label like "qwen" is not an identity; a silent server-side model swap must be visible.
  • Sampling params - {temperature, top_p, seed, max_tokens}, recorded as values. temperature=0 assumed but unrecorded is not pinned.
  • Context manifest - ordered [{path, content_sha256}], enough to reconstruct the exact prompt surface.
  • Cost actuals - {prompt_tokens, completion_tokens, usd}, parsed from provider usage, not estimated.
  • Per-iteration check + commit - the check command, its exit code, and the commit it ran against. Per Invariant T2, the receipt must answer which oracle, at which commit, under which pinned model, having spent how much, with which guards intact.
  • Computed halt_reason - the halt reason is computed from observed state, never a flag.
  • Achieved determinism confidence - the lower bound from the confidence bound probe, not a pass/fail.
{
"run_id": "2026-06-25T10-00Z-fix-auth",
"model_identity": {
"provider": "anthropic", "model_id": "claude-opus-4",
"version": "build-20260601", "quantization": null,
"runtime": "api", "endpoint": "https://api.anthropic.com"
},
"sampling": { "temperature": 0, "top_p": 1, "seed": 0, "max_tokens": 4096 },
"context_manifest": [{ "path": "pkg/auth/token.go", "content_sha256": "7e1..." }],
"cost_actuals": { "prompt_tokens": 6120, "completion_tokens": 880, "usd": 0.41 },
"iterations": [
{ "i": 7, "check_cmd": "go test ./pkg/auth", "exit_code": 0, "commit": "a1b2c3d", "guards_intact": true }
],
"halt_reason": "success_condition_met",
"determinism_confidence": 0.99
}

Replay verifies a verdict. Re-execute re-runs the loop.

Section titled “Replay verifies a verdict. Re-execute re-runs the loop.”

These two verbs must never blur. Conflating them is the single most common overclaim in this space.

replayreexecute
Question answered”Does this receipt’s verdict still hold?""Is this outcome reproducible in distribution?”
What it runsthe deterministic check against the recorded end-statethe live agent loop, again
Agentnoneyes
Budgetfreeburns budget; --confirm-gated
Determinismdeterministicnon-deterministic
Resultfingerprint match against the receipta statistical match, never byte identity
VerbVERIFYRE-RUN

replay re-runs only the external check against the recorded commit and compares the fingerprint {exit_code, stdout_normalized, stderr_normalized} to the one in the receipt. No agent. No spend. It proves the verdict, offline.

reexecute runs the whole live loop again. The LLM samples, so the trajectory differs every time. It can report a distributional match, nothing more.

Resume revalidates before the first agent call

Section titled “Resume revalidates before the first agent call”

Because progress lives on disk, a loop is resumable: the contract’s resume path can pick it back up without losing place. But on-disk state can go stale - the model behind an endpoint can change, and the test-determining surface can drift between runs.

So on resume, the contract requires the runtime to revalidate determinism_probe and metric_integrity before the first agent call, and to halt on drift rather than build on a stale anchor:

  • The recorded model_pin is re-resolved against the live model-identity tuple. A mismatch is model_drift_detected (class 19, liveness/drift).
  • The metric-integrity gate snapshot is re-checked against the immutable t0 baseline. A change in the collected-test set, assertion count, or protected-manifest hash halts before any new work lands.
  • Anything that cannot be resolved fails closed with workspace_invalid (class 30), not a silent best-effort resume.

An unacked escalation state is treated as indistinguishable from a crash: resume blocks until a signed ack clears it, so a paged human is never bypassed.

CapabilityStatus
init / status / check / step commands, stable exit codesShipped
Durable schema-versioned .loop_state.json (SPEC section 8)Shipped
Typed JSONL receipt with full pinningShipped (core) - live cost metering Planned
probe-check confidence bound feeding determinism_probeShipped (core) - perturbation + in-loop monitor Planned
replay (verify) / reexecute (re-run, --confirm-gated)Shipped
attest / ack signed receiptsShipped
Metric-integrity gate revalidated on resumeShipped (core) - assertion-count / manifest-hash / coverage-floor Planned

State on disk is what makes the rest of the discipline possible. Pair it with determinism for a trustworthy check and with halt reasons for a computed stop.