Harness
Concepts

Every point where the loop stops and waits for a person, and why it stops there and not elsewhere.

View raw Markdown · llms.txt · For agents

The loop runs unattended. It stops in exactly these places, and the rule behind all of them is the same: a human decides where the machine would be acting on the world, or acting on itself.

GateWhereHow it is passed
The PRDloop planloop plan approve <id>
The technical designloop plan, after the voteloop plan approve-design <id>
The queue entryloop plan decompose --create writes issues in the entry stateMoving them to the ready state
Merge approvaldeliver, when merge.requireHumanApprovalApproving the pull request on GitHub
Releasereleaseloop release approve, bound to a head sha
A blocked issueAfter the fix rounds run outReading the PR comment and deciding
An escalationUnverifiable contract, no plan consensus, stuck worker, budget exceededAnswering on the ticket
A paused issue or stageAfter repeated failuresloop resume <issue> or loop resume --stage <stage>
Publishing an agentThe retro may propose; it never publishesA human, to the registry

Why the design gate exists even after a vote

Three agents agreeing is evidence, not authority. Everything built afterwards inherits the design, so the cost of a wrong one is every issue decomposed from it. Consensus makes the design worth reading; it does not make it someone's decision.

Why the release approval binds to a sha

An approval that outlived its commits is a rubber stamp. The approval records the head it was given for; if anything merged after it, the batch is different and needs its own approval. A successful promotion spends the approval, so nothing merged later rides on it.

Escalations are not retries

The loop never answers its own question by trying again more cheaply:

  • an issue over budget.perIssueTokens escalates, it does not continue with less headroom;
  • a plan without consensus after maxCycles escalates, with the objections still standing;
  • a contract with a blocking ambiguity escalates instead of dispatching a worker to guess;
  • an issue that failed resilience.maxConsecutiveFailures times is paused and labelled, so the next tick does not pick it up and fail the same way.

Every one of those writes an event and, where a channel is configured, calls it. notifications.events lists which types reach the channel; onEscalate always does, whatever that list says.

What the loop does without asking

Merging a pull request whose checks are green, whose review is clean and whose two definition-of-done lists are proven — when merge.auto is on and requireHumanApproval is off. That is the one action it takes on the world by itself, and it is precisely the one that every other gate above exists to make safe.

On this page