Loop or graph: a sensor-gated coding-agent loop measured against the 2026 harnesses
For an unattended coding agent, the deciding question is who declares the work done. This page is a reference for that decision: what a sensor-gated loop with a governor adds to a plain agent loop, where graph frameworks fit (at the outer layer, ordering loops and deterministic steps), and what the primary sources from 2026 actually say once you read them rather than their summaries. It is the sibling of Benchmarking local models for agent work, which picks the model that sits in the loop, and of Scheduling one GPU between LLM, image and training work, which owns the hardware the loop runs on. Process rules enforced at the tool call are covered separately in the guard-extension page.
Provenance
Section titled “Provenance”The journal numbers were measured from the run journal on 2026-10-14, filtering out bench and trial runs and counting only runs with a recorded result. The loop’s mechanisms are read from its own docs and test suite; a mechanism that only a scripted agent exercises is marked design-only, and a mechanism exercised by a live run is marked tested. The gap-to-commit mapping and the suite counts come from the implementation plan in the dotfiles repo (private), cited as text. The discourse section is asserted - read from the primary sources it links - not re-measured. Anything the research left unverified is named as unverified rather than smoothed over.
- For unattended changes, the load-bearing part is one fixed inner loop gated by objective sensors (exit codes), with a governor outside it that can roll back, fence the write scope, and climb a model ladder when the loop stalls. Graphs do not replace this; they sit one layer up, ordering several loops and the deterministic steps between them.
- The 2026 discourse converges on that split once you read the primary sources. Böckeler’s guides-and-sensors vocabulary and OpenAI’s “Harness engineering” post both put steering and feedback loops at the centre of a trustworthy agent12. Stripe’s minions are a blueprint graph of deterministic and agent nodes, but the deterministic nodes are exactly the sensors a loop runs3.
- The plain Ralph loop has no objective gate: a hook re-feeds the prompt until the agent emits a completion promise45. That is fine when a human reviews the diff anyway and wrong when “done” must be decided by something other than the model’s self-report.
- Seven weeks of live runs (100 with a result) came out 84 pass, 65 of them on iteration 1, with 6 model-ladder climbs across 5 runs. Agent turns took 15.0 h against 1.23 h of sensors, so the place to spend effort is the agent turn, not the sensors.
- The loop’s governor does five things the surveyed tools do not document: an escalation ladder keyed to stalls, a negative-knowledge history of rolled-back attempts, a write-scope fence with ref/index guards, process-group reaping, and signed governor receipts. That holds for the tools surveyed, not the whole field.
Topology
Section titled “Topology”The same thing as text:
- Context commands and upstream summaries feed the prompt once, outside the jail.
- Each iteration is a fresh agent turn (a fresh
pi -p) on the current ladder rung. - The write-scope fence reverts out-of-scope edits; ref/index guards stop the agent from moving the checkpoint with its own git commands.
- Sensor
fixcommands (formatters, autofix) run after the fence, before the sensor pass. - Sensors gate on exit codes; the decide step keeps, rolls back, or escalates.
- The governor - checkpoint/rollback, jail, budgets, the ladder, signed receipts -
sits outside the loop. The outer graph orders several loops and the
cmdnodes between them, passing each loop’s summary to its dependents.
Which do I pick
Section titled “Which do I pick”| Option | What it is | When it fits | When it does not |
|---|---|---|---|
| Plain agent loop (Ralph) | A Stop hook re-feeds the same prompt when the agent tries to exit; completion is a text promise the agent emits. No objective gate45. | Interactive or lightly supervised work where a human reviews the diff; prototyping. | Unattended changes, where “done” has to be decided by something other than the model’s self-report. |
| Sensor-gated loop with a governor (this one) | Objective exit-code sensors decide completion; the governor adds checkpoint/rollback, a write-scope fence, and a model escalation ladder. | Unattended, test-fenced changes; greenfield modules; doc and lint conformance. | Unfenced blast radius; tasks with no spec to gate on. |
| Graph / workflow engine (LangGraph, ADK graphs, Archon YAML DAGs, Stripe blueprints) | A fixed graph of deterministic and agent nodes, with ordering, parallel fan-out, and per-node model routing6783. | Several independent tasks, or mixing deterministic steps (codegen, migration) with agent steps, when you know the shape up front. | One tightly-coupled task, where the graph is coordination overhead on top of the loop. |
| Durable agent framework (Vercel eve on the Workflow SDK, Temporal-style) | Filesystem-first durable agents whose turns checkpoint so they survive crash and redeploy; the sandbox sits outside the credential-holding loop9. | Long-running multi-step agents that must park for hours (approvals, sign-ins) and resume; credential isolation matters. | It has no objective done-check, so it does not by itself tell you the work is correct. |
The discourse, from the primary sources
Section titled “The discourse, from the primary sources”Ralph, and what a plain loop is missing
Section titled “Ralph, and what a plain loop is missing”In its original form Ralph is a Bash loop: while :; do cat PROMPT.md | <agent>; done
pipes the same prompt file into a new agent process, again and again4.
Anthropic’s Claude Code ships a plugin version that runs inside one session: a Stop
hook intercepts the exit attempt and feeds the same prompt back until the agent
emits a completion promise5. The loop never
judges the work - the agent decides it is done by saying so. That is the gap every
later system in this page closes, one way or another: move the completion decision
out of the model and into something the operator controls.
Harness engineering: guides and sensors
Section titled “Harness engineering: guides and sensors”Böckeler’s framing is the vocabulary this loop uses. A harness is everything in a coding agent except the model; its outer layer serves two goals - raise the probability the agent gets it right first time, and provide a feedback loop that self-corrects before the result reaches human eyes1. The controls split two ways:
- Guides (feedforward) anticipate behaviour and steer before the agent acts.
- Sensors (feedback) observe after the agent acts and let it self-correct, and are most powerful when their output is optimised for LLM consumption - her example is a custom linter whose message embeds the remediation, “a positive kind of prompt injection”.
Each control is computational (deterministic, run by the CPU: tests, linters,
type checkers) or inferential (semantic, run by a model: AI review, LLM-as-judge).
Computational sensors are cheap enough to run on every change; inferential ones add
judgment at higher cost and non-determinism. The split matters for this loop because
its keep/rollback decision is a computed comparison of failing counts, with the
inferential judge feeding a SCORE only as a tie-break - the deterministic signal
dominates.
OpenAI’s harness engineering in practice
Section titled “OpenAI’s harness engineering in practice”OpenAI’s team built and shipped an internal product with zero lines of human-written code, on the order of a million lines across roughly 1,500 merged pull requests at 3.5 PRs per engineer per day2. The mechanisms they describe are the same family as this loop’s:
- Lint messages that teach the fix. Custom linters are written so the error message injects the remediation into agent context - Böckeler’s “positive prompt injection” in production.
- A bootable app per worktree, driven over CDP. One app instance per change, with DOM snapshots and screenshots, so the agent can reproduce a bug and validate a fix directly.
- Observability as agent context. Logs and metrics exposed to the agent via LogQL and PromQL on an ephemeral per-worktree stack, so prompts like “startup completes in under 800 ms” are checkable.
- Doc-gardening agents. A recurring background agent scans for stale documentation and opens fix-up pull requests.
- Ralph-style review loops. An engineer describes a task, the agent reviews its own change locally, requests further agent reviews, and iterates until all reviewers are satisfied - “effectively this is a Ralph Wiggum Loop”. Their stated conclusion: the hardest problems are “designing environments, feedback loops, and control systems”.
Two of those - the observability sensors and the doc-gardening loop - map directly
onto this loop’s metric-assert sensor and its weekly garden job, described below.
Graph engineering, and the counter-evidence
Section titled “Graph engineering, and the counter-evidence”The graph camp puts the structure first: a directed graph whose nodes are agents or plain functions, with parallel fan-out, routers, shared state and checkpointing, as in Google’s ADK graph workflows and LangGraph76. The underlying distinction is Anthropic’s workflows-vs-agents split: a workflow is a fixed graph of steps, an agent is a loop with tools that the model directs10. Read from the primary sources, the two camps agree on the constraint and differ only on the default:
- LangChain’s Open Deep Research dropped parallel section-writing and keeps multi-agent orchestration only for research11.
- Cognition’s “Don’t build multi-agents” argues multi-agent systems are fragile - decision-making too dispersed, context not shared thoroughly enough12.
- Anthropic’s own multi-agent research post, the strongest graph result, says the pattern excels at “breadth-first queries that involve pursuing multiple independent directions simultaneously”, and that “most coding tasks involve fewer truly parallelizable tasks than research”13.
That is the boundary this page draws: graphs at the outer layer, where work is genuinely several independent loops or deterministic steps, and one fixed inner loop where it is not. The unverified claim that a LangGraph workflow fails 24% of tasks against 11.5% for a loop harness, and the “scaffold moves scores 48 points vs 5 for a model change” claim, both come from a single untranscribed video and have no primary source; they are left out except as this note.
Stripe minions: blueprints are graphs with deterministic sensors
Section titled “Stripe minions: blueprints are graphs with deterministic sensors”Stripe’s unattended minions produce more than a thousand merged pull requests a week, human-reviewed
but containing no human-written code3. Their orchestration
primitive, the blueprint, is a graph that interleaves deterministic code nodes
(“Run configured linters”, “Push changes”) with agent nodes (“Implement task”,
“Fix CI failures”). The point of the deterministic nodes is explicit: they
guarantee a subtask runs, and they save tokens and CI cost by not letting the
model decide whether to lint. Stripe runs a subset of linters as a deterministic
node in the local loop before pushing, then one round of full CI with autofix,
then one more agent fix round, then the branch goes to a human. The “one or two
CI rounds, then a human” cap is the same budget reasoning as this loop’s
maxIterations and model ladder - diminishing returns on indefinitely many
rounds.
Vercel eve: durability and credential isolation, not completion
Section titled “Vercel eve: durability and credential isolation, not completion”eve is a filesystem-first durable agent framework: an agent is a directory of
markdown and TypeScript, and each turn runs as a durable workflow on the Workflow
SDK, so a session survives crash, timeout and redeploy by checkpointing at each
step9. It splits execution into an app runtime that holds the model, the loop
and the credentials, and a sandbox that runs the bash and file tools - so the
sandbox never sees the provider key. That is a real answer to a real problem this
loop does not solve (see Limits). But eve has no objective done-check: completion
is the agent’s own stop condition, not a sensor. Its in-flight refactor (issue
#512) is to decouple the loop from durable execution behind a pluggable Loop
interface - evidence that the loop itself is the part being isolated and made
testable, not replaced by a graph.
What this loop has that the surveyed tools do not document
Section titled “What this loop has that the surveyed tools do not document”Scoped to the tools the research covered (Ralph, Claude Code, Codex, ADK, LangGraph, Archon, Mastra, CrewAI, Goose and Stripe’s fork of it, eve, OpenHands, SWE-agent, Aider, smolagents, DSPy, Copilot agent mode). Per-node or per-recipe model routing is common; the following are not documented in any of them:
- Escalation ladder keyed to stalls. The loop climbs to the next model rung
after
stallPatienceno-progress iterations, not on a fixed schedule. The surveyed tools let you assign a model per node or per recipe; none documents climbing in response to a detected stall. - Negative-knowledge history. A rolled-back attempt is injected into later
prompts naming what it broke, so a fresh iteration cannot repeat a proven dead
end. Without it, feedback is built from the best-known-good state and the next
iteration cannot see the failure that caused the rollback - a recorded run walked
into the same
gofmtfailure again on the next iteration for exactly that reason. - Write-scope fence plus ref/index guards. Out-of-scope edits are reverted each
iteration, and agent-run
git commit/reset/stash/skip-worktreecannot move or destroy the checkpoint. Stripe solves isolation with a quarantined devbox; this loop solves it per-iteration inside one repo. - Process-group reaping. A wall-clock budget per sensor and per agent turn, with the process group killed past budget, so nothing a sensor or agent backgrounds survives it.
- Signed governor receipts. Each governor step (
sense,checkpoint,rollback,scope-check) can emit a signed JSON receipt against a nonce, which an outside driver verifies withverify-receipt. It exists so a separate orchestrator (the bundled Workflow driver) can drive the loop and still prove each governor decision was the loop’s, not an edit.
Gaps found and closed
Section titled “Gaps found and closed”The 2026-10-13 review measured the loop against the field above and produced eight gaps, shipped in value order, without turning the loop into a workflow engine. The commits are in the dotfiles repo (private), cited as text.
| Gap | Source of the idea | What shipped | How it was checked |
|---|---|---|---|
G1 sensor fix command | Stripe autofix / local lint node | a21f303 | live run (a local-model run on a test repo: the formatter ran after the agent turn) |
| G2 zero-change iteration = forced stall, sensors skipped | journal: zero-edit iterations read as progress | a21f303 | scripted agent (loop-zero-change.integration.test.ts) |
G3 manifest context commands + --context-file | Stripe pre-hydration; OpenAI context legibility | f10d7e5 | live run (the same test-repo run: context injected before iteration 1) |
G4 scored sensors (SCORE:, judge emits it) | SKILL.md limit: a binary judge has no gradient | 507b837 | live run (garden e2e: judge SCORE 15 -> 25 -> 75 kept each iteration at an equal failing count) |
G5 graph cmd nodes + upstream summaries | Archon / Stripe blueprints mix deterministic and agent nodes | 7721837 | scripted agent (graph-cmd.integration.test.ts) |
G6 metric-assert PromQL/LogQL sensor | OpenAI per-worktree observability | ebbcada | stub tests, then a live check against the real Prometheus and Loki (2026-10-14) |
G7 garden: weekly checks-only timer + garden --fix on demand | OpenAI doc-gardening; the local LLM is often not running | 7b3682b, ee8f6a9, f85e663, dcb47f6; e2e diff 5777cac | live run (third e2e passed on iteration 1) |
| G8 best-of-N on cloud rungs | no measured need | deferred | n/a - revisit when needed-escalation or thrash exceeds 10% of runs |
The bugs the live runs found, which the gaps did not predict:
| Bug | What it looked like | Fix |
|---|---|---|
Untracked .pi/ read as a dirty tree | every run refused to start in a repo with no tracked .pi/: the loop writes its run log there before the clean-tree check, and git reports the new directory as one entry the loop did not recognise | 9b8de15 |
Bare --context-file dropped context | a trailing --context-file with no path overwrote the paths collected so far, and the run went ahead without context | 9b8de15 (now a usage error) |
The write-scope fence exempted all of .pi/ | an agent edited the lint config to waive its own warnings | 639facd narrowed the fence to the loop’s own harness files plus ! exclusions |
| A timed-out judge read as lateral progress | the partial output of a killed judge differed every time, so an equal failing count with a “new” fingerprint was kept as progress | b7531e5 - a timed-out sensor fingerprints as a constant |
| The local-model health check never matched | the garden precheck grepped for Health: ok against a status line padded with spaces, so every run fell back to the cloud model | ee8f6a9 |
G7 applies the thesis to operations. The weekly garden timer only checks - skills-lint and stow-drift -
and files cards; it runs no model, because the local LLM is often not running and an
unattended run must neither swap the GPU away from image or training work nor spend
cloud credit on its own. garden --fix runs the loop on demand in a worktree, with
the fence keeping the agent off vendored skills and the lint config. The agent never
commits; a human reviews the card and merges. It is the same division of labour the
graph camp describes, achieved with a timer plus an on-demand loop instead of a graph.
Reading the numbers
Section titled “Reading the numbers”Measured 2026-10-14 from the run journal, non-bench and non-trial runs with a recorded result, spanning 2026-08-24 to 2026-10-13:
- 100 runs: 84 pass, 12 interrupted, 3 fail, 1 crashed. 65 of the 84 passes finished on iteration 1 (77%).
- 164 iterations, 37 of them zero-change (22.6%). A zero-change iteration is an agent that read for minutes and wrote nothing; before G2 it read as progress, now it is a stall that skips the sensor pass.
- Agent 15.0 h vs sensors 1.23 h. The agent turn is where the time goes; the sensors are already cheap. Effort belongs on the model and its context, not on speeding up the gates - which is why the bench page picks a fast local model for loops on the grounds that failure is cheap.
- 5 runs climbed the model ladder (6 climbs total). The
needed-escalationfailure mode appears on 3 runs. The low rate is the recorded justification for deferring best-of-N: parallel worktree attempts are a large change with no measured need. - Failure modes: scope-fighting 6, agent-silent 4, budget-exhausted 3, thrash 3, needed-escalation 3, sensor-timeout 1, agent-error 1 (labels on the 19 runs that did not pass cleanly; a run can carry several). Scope-fighting - at least one iteration touched a path outside the write scope - is the most common; each instance is the fence reverting an edit the agent should not have made.
The journal holds one zero-change iteration that was read as progress: a 5 -> 4 move
from a flaky sensor with no edit behind it. Since G2 such an iteration is a stall by
definition, its sensors are skipped, and the report marks it noChange.
Limits that remain
Section titled “Limits that remain”- The jail holds the model credentials.
pi -pboth calls the model and runs the tools inside the jail, so the provider key is readable by any command the agent runs there. eve keeps the loop and the key outside and hands only tool calls to a sandbox; matching that needs a tool-only sandbox inside the harness, not a loop change. Masked secret dirs cover everything else. - Binary sensors have no gradient. A binary sensor contributes a pass/fail but
no direction; the keep/rollback decision uses the failing count, and the
SCOREtie-break only exists when a sensor (the judge, since 2026-10-13) prints one. An iteration that resolves five of the judge’s seven objections is still indistinguishable from one that resolves none on a binary judge. - Green computational sensors prove the checks pass, not that the code is
right. If the model wrote the tests too, that is a closed loop. The inferential
judge and
pixel-diffraise confidence; they do not remove the need for a clear spec, and a vaguetaskconverges on green-but-wrong. - Not for unfenced blast radius. This is a greenfield-module and test-fenced tool, not “loop on the payments service unattended”.
- Escalation can be triggered by a harness defect, and then it is the wrong
remedy. If the loop never told the agent what it was being judged on, a stall
reads as “the model is too weak”.
loop report --prompt Nshows the exact text an iteration saw, which is the fastest way to tell the two apart. - The governor’s later rungs are design-only. Beyond the first escalation and the negative-knowledge history, the ladder and receipts are exercised only by scripted agents in the integration suite - designed and unit-tested, not field-proven.
Decision guide
Section titled “Decision guide”The same decision as text:
- Is the change unattended? No - a human reviews the diff, so a plain Ralph loop is enough.
- Do objective gates exist (build, lint, test, a spec you can assert)? Yes - a sensor-gated loop with a governor.
- No clear spec - is the work several independent tasks or deterministic steps between them? Yes - an outer graph of cmd and loop nodes.
- Not several tasks, but the run must park for hours or isolate credentials from the sandbox - a durable agent framework (eve).
- None of the above - the work is one task with no gate, and the fix is to tighten the spec, not to add a graph.
Evidence table
Section titled “Evidence table”| Claim | How it was checked | Tested vs design-only |
|---|---|---|
| 100 runs, 84 pass, 65 first-try, 164 iterations, 37 zero-change, 15.0 h agent vs 1.23 h sensors, 6 ladder climbs | measured from the run journal, 2026-10-14 | measured |
| The loop reverts out-of-scope edits and blocks agent git mutations of the checkpoint | read from governor docs; loop.integration.test.ts | tested |
| Zero-change iterations are stalls and skip the sensor pass | loop-zero-change.integration.test.ts (scripted agent) | tested |
A sensor fix command runs after the fence, before the sensor pass | loop-fix.integration.test.ts; also a live local-model run | tested |
SCORE: breaks the keep/rollback tie | loop-score.integration.test.ts; garden e2e observed 15 -> 25 -> 75 | tested |
Graph cmd nodes and upstream summaries reach a dependent loop’s prompt | graph-cmd.integration.test.ts (scripted agent) | design-only |
metric-assert gates a PromQL query or a LogQL line count | stub tests; live check against the real Prometheus and Loki, 2026-10-14 (a true and a false PromQL assertion, a LogQL round trip) | tested |
| The escalation ladder and negative-knowledge history work beyond the first rung | loop.integration.test.ts (scripted agent) | design-only |
| Signed governor receipts verify a step against a nonce | receipt.test.ts | design-only |
| The surveyed tools do not document the five governor mechanisms | read from the primary sources of each surveyed tool, 2026-10-13 | asserted, scoped to those tools |
| Stripe blueprints, OpenAI’s per-worktree observability, and eve’s credential split | read from the linked primary sources | asserted |
Related docs
Section titled “Related docs”- Benchmarking local models for agent work - which model sits in the loop, and why failure being cheap matters.
- Qwen3.8-27B for agentic work - what actually runs on the local rung, and why speculation degrades under loop churn.
- Scheduling one GPU between LLM, image and training work - why an unattended run must not swap the GPU, the constraint behind
garden’s checks-only timer. - Guard extensions that enforce prose and process rules - the mechanical rule enforcement this loop’s governor sits beside.
- A kanban board for coding agents - where the garden timer files its cards.
References
Section titled “References”-
Birgitta Böckeler, “Harness engineering for coding agent users,” martinfowler.com, 2 April 2026. https://martinfowler.com/articles/harness-engineering.html ↩ ↩2
-
Ryan Lopopolo, “Harness engineering: leveraging Codex in an agent-first world,” OpenAI, 11 February 2026. https://openai.com/index/harness-engineering/ ↩ ↩2
-
Alistair Gray, “Minions: Stripe’s one-shot, end-to-end coding agents - Part 2,” Stripe, 19 February 2026. https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2 ↩ ↩2 ↩3
-
Geoffrey Huntley, “Ralph Wiggum as a ‘software engineer’.” https://ghuntley.com/ralph/ ↩ ↩2 ↩3
-
Anthropic, “Ralph Wiggum plugin,” claude-code repository. https://github.com/anthropics/claude-code/tree/main/plugins/ralph-wiggum ↩ ↩2 ↩3
-
LangChain, “LangGraph.” https://github.com/langchain-ai/langgraph ↩ ↩2
-
Google, “Agent Development Kit (ADK).” https://github.com/google/adk-python ↩ ↩2
-
Cole Medin, “Archon.” https://github.com/coleam00/Archon ↩
-
Vercel, “eve” (README and “Execution model and durability”). https://github.com/vercel/eve and https://github.com/vercel/eve/blob/main/docs/concepts/execution-model-and-durability.mdx ↩ ↩2
-
Anthropic, “Building effective agents.” https://www.anthropic.com/engineering/building-effective-agents ↩
-
LangChain, “Open Deep Research.” https://www.langchain.com/blog/open-deep-research ↩
-
Walden Yan, “Don’t build multi-agents,” Cognition, 12 June 2025. https://cognition.ai/blog/dont-build-multi-agents ↩
-
Anthropic, “How we built our multi-agent research system,” 13 June 2025. https://www.anthropic.com/engineering/multi-agent-research-system ↩