1
TaskState is the source of truth
The model’s message history is derived from
TaskState each turn — not the
other way around. State is explicit, structured (pydantic), and append-mostly,
so a run is inspectable and replayable.2
The runner owns all mutation
Tools are pure-ish: they receive a run-scoped
RunDeps, touch the filesystem
or run commands only through the Workspace handle, and return a ToolResult.
They never mutate TaskState. The runner applies results after logging and
permission checks.3
'Done' is a proposal the verifier disposes of
A final answer marks a task ready for verification — it never ends the run.
Only the harness-owned
Verifier sets outcome = "success", and only on
positive external evidence. The model never self-certifies.4
Control hooks vs. observation events are a hard line
The permission gate is an awaited control hook that can block or redirect the
loop. The event emitter is observation-only: synchronous, fire-and-forget, and
unable to alter control flow. Never route control through the emitter.
5
Everything is reversible and observable
Work happens on a tracked, path-confined
Workspace; every edit is an
inspectable diff. An append-only JSONL event log gives replay, debugging, and
eval data for free.Two axes, kept separate
Conflating them is what leaves “ran out of budget” and “failed verification”
ambiguous. Keeping them separate keeps the loop’s exit conditions legible.