Start With a Gate, Not a Goal
A loop that stops because the agent says it is done is not autonomous engineering. It is unattended self-reporting. The central move is to turn the definition of done into a state transition owned by the harness: `working -> verifying -> passed` or `working -> verifying -> failed -> working`. The model can propose actions, interpret failures, and choose repairs, but it should not be the authority that closes the loop.
Write the spec as small buckets with explicit exit criteria. For deterministic work, those criteria are commands and thresholds: unit tests pass, typecheck is clean, Playwright records the target flow, Lighthouse stays under a latency budget, no new production error signature appears after replay. For subjective work, such as architecture cleanup or documentation quality, still force structure: a rubric, required evidence, forbidden regressions, and a judge result that maps to pass/fail with reasons. The weaker pattern is “improve this until good.” The stronger pattern is “continue until these probes produce these artifacts and this reviewer cannot cite a blocking violation.”
Make Proof Artifacts Hard to Fake
Once the gate owns completion, the next failure mode is shallow verification. Agents learn to summarize expected success, rerun partial commands, or ignore inconvenient logs. Require proof artifacts that downstream code can inspect: raw test output, coverage deltas, trace files, screenshots, Playwright videos, benchmark JSON, container logs, migration dry-run output, commit SHAs, and hashes of generated reports. Store them under a run id and make the verifier read from disk or CI, not from the implementing agent’s narration.
For browser and ops loops, proof should include environment observations, not just code checks. A service-health loop might first hit HTTP, then inspect containers only if health fails, then branch on structured JSON like `website_up`, `needs_approval`, `commands_requested`, and `applied_fix`. State-changing commands should sit behind an approval gate unless the action is pre-authorized and narrowly scoped. The same principle applies to code: destructive shell commands, protected directories, and credential-touching paths belong in pre-tool hooks. If a prompt says “never edit X,” the agent may forget. If the tool layer rejects edits to X, the loop has a real invariant.
Separate Implementers From Reviewers
After proof exists, isolate judgment. Do not let the same context window that wrote the patch decide whether the evidence is sufficient. Use a state machine with roles: planner, implementer, verifier, reviewer, and retrospective. The implementer gets the smallest useful context and the current failure. The verifier runs deterministic checks. The reviewer receives the spec, diff, and proof artifacts, then looks for mismatch, hidden scope expansion, and missing tests. Only the harness advances state.
For hard tasks, fan out implementation attempts in separate worktrees, then synthesize or run a tournament between candidates. Head-to-head comparison is often less biased than asking one judge to score a long list. For brittle bugs, loop isolated theories until one reproduces the failure or produces a passing fix with a regression test. For architectural work, use adversarial verification: a skeptic agent cross-references the patch against a rubric such as dependency direction, duplication budget, public API compatibility, and migration risk. LLM judges are useful here, but only as one gate in a chain. They should cite concrete files, diffs, and artifacts, and their verdict should be capped by deterministic blockers.
Route Models and Memory Through the Harness
Hard gates make model routing simpler because each state has a job. Cheap models can classify tasks, extract changed files, summarize logs, or update run metadata. Stronger models should handle multi-step diagnosis, ambiguous failures, and reviewer passes where tool use and judgment matter. Do not burn frontier tokens on every retry. Retry policy should include escalation: same model for mechanical fixes, stronger model after repeated verifier failures, human review after repeated judge disagreement or risky state changes.
Memory should be just as disciplined. Large skill dumps inflate context and make agents less precise. Keep persistent memory as curated gotchas, architectural constraints, tool maps, and failure postmortems. A retrospective agent can read execution logs after each run and propose updates, but those updates should be reviewed or gated like code. The useful memory is not “everything about the repo.” It is “the migration test must run with tenant fixtures,” “do not restart the hosting container,” “the LSP server gives better symbol navigation than grep for this language,” and “benchmark noise above 8 percent requires three samples.” Treat repeated agent mistakes as harness bugs: missing rule, missing hook, missing artifact, missing verifier, or wrong model for the state.
Control Cost by Bounding the State Machine
A hard-gated loop can still waste a night. Cost control belongs in the design, not in hope. Set per-state budgets, max iterations, wall-clock limits, and artifact retention rules. Cache expensive observations, shard tests by affected surface, and run cheap checks before expensive ones. A loop trying to reach sub-50ms page loads should not run the full suite after every CSS tweak; it should benchmark the target pages, inspect regressions, then promote to broader CI only when local thresholds pass.
The stop condition should distinguish success, exhaustion, and escalation. Success means every required gate passed and artifacts are attached. Exhaustion means the loop hit a budget and produced a concise failure packet: spec, attempts, diffs, logs, current hypothesis, and the exact failing gate. Escalation means the next transition requires approval, a stronger model, or a human decision. This is what makes overnight autonomy tolerable: not that the agent always finishes, but that it never gets to declare victory without evidence, and when it fails, it fails into a state you can resume from.
The frontier pattern is not a smarter prompt. It is a harness where definitions of done are executable, evidence is durable, reviewers are independent, and pass/fail transitions are owned by code. The agent remains powerful precisely because it is no longer trusted with the one decision it is worst at making alone: whether its own work is finished.
Key takeaways
- The harness, not the implementing agent, should own the transition from working to done.
- Every gate should produce inspectable proof artifacts such as logs, traces, screenshots, benchmark JSON, hashes, or CI output.
- Use deterministic checks first, reviewer agents second, and LLM judges only where the definition of done is genuinely subjective.
- Run implementers, reviewers, and skeptics in isolated contexts to reduce self-preference and goal drift.
- Keep memory small and curated around gotchas, constraints, and postmortems rather than dumping full documentation into every run.
- Bound autonomous loops with budgets, retry policy, escalation states, and failure packets that make the run resumable.