# Agent harness

Independent practice · Current · Published October 6, 2026

A repository-level operating system for coding agents. It makes the correct start, scope, proof, and record cheaper than the alternatives, and makes the alternatives fail.

## Thesis

A coding agent working inside one repository over many sessions fails in predictable ways. It starts before it has read. It drifts out of scope. It calls something wired that nothing runs. It asserts an API from memory. It repeats a mistake an earlier session already paid for. Each of those had a written rule against it when it happened, and the rule was in force, understood, often quoted, and broken anyway.

The harness is the response. Where a rule can be made mechanical, it becomes a check, and only what a script cannot express stays as prose. A rule the agent agrees with does not change its behaviour hours into a long session, when recollection starts to feel like knowledge. A check does not get tired.

Keeping a few units separate is most of the design. The unit of orientation is a branch inside a conversation. The unit of design is an area overview. The unit of history is a dated ledger row. The unit of scope is a catalog identifier under a stable prefix. The unit of proof is a guard together with a mutation that breaks it.

## Where enforcement lives

Enforcement runs on three surfaces with different powers.

- **Hooks in the agent host** run on every session start, tool call, file read, and shell command. They can inject context and deny a call before it executes, but they cannot see the diff or tell whether a claim is true.
- **A local git hook** runs on every commit and can refuse it, using every guard that needs only the working tree.
- **Continuous integration** reruns the same guards on every pull request, against the branch diff with full history.

Only the host hooks act before work happens. Always-applied rule files and a contributor constitution act on every turn, but only as text. The stacking is deliberate: an agent can ignore one layer, and should not get past all of them. Removing a layer is a scoped change with its own identifier, not a cleanup.

```mermaid
%% Where the harness acts: the host before the work, the hook and CI after it.
flowchart TD
  host["Agent host hooks"] -->|"inject, deny"| turn["Agent turn"]
  rules["Always-applied rules"] -->|"every turn"| turn
  map["Map, read first"] --> turn
  turn --> hook["Pre-commit hook"]
  hook --> ci["CI on the pull request"]
  ci --> review["Human review"]
```

## The orientation gate

Orientation is four steps in a fixed order: the map, the inventory, the overview and ledger of the area the task belongs to, and only then a targeted read of code. The gate makes the shortcut, a blind search before the map is open, impossible rather than discouraged.

Each conversation keeps a small state record of its branch and of which required reads have happened.

- A session start or a branch change resets it and re-injects the pointer to the map.
- While it is pending, the host denies writes, searches, subagent spawns, and any shell command outside a narrow allowlist of read-only git commands.
- A read clears a requirement only when its path resolves inside the repository to exactly the required file. A same-named file elsewhere does not count.
- A malformed event is a deny.

```mermaid
%% The orientation gate: closed until the required reads, and closed again on every branch change.
stateDiagram-v2
  pending: Pending, writes and searches denied
  cleared: Cleared, tools allowed
  [*] --> pending: session start or branch change
  pending --> cleared: required reads done
  cleared --> pending: checkout
```

The gate does not judge whether the agent understood what it read. The rules and review do that. The gate closes the cheapest loophole, which is starting before reading.

## Doctrine and ledger

Each documentation area is a program, not a wiki. It has two kinds of file, and they are not interchangeable.

- **The overview is doctrine:** what the area is and is not, its locked design, and what done means. It has no todos and no future tense.
- **The ledger is history:** dated rows, newest first. Each names what landed, the identifiers it landed against, and the proof: a pull request, a commit, a command and what it printed, or an honest wait. A ledger is never rewritten to look finished.

The split lets an agent inherit decisions without inheriting the confusion of how they were reached. When the ledger and the code disagree, the code wins and the ledger is stale.

The map that indexes the areas has a generated part. A drift guard fails whenever the generated part, the counts, the list of areas, or the set of always-applied rules differs from what is on disk. Counts are never typed by hand.

## Masters, children, and a frozen catalog

A master plan scopes a program. It locks the design, assigns a stable identifier prefix, and freezes an original catalog of identifiers. It has no todos and implements nothing.

A child implements one cut in one pull request. It cites the identifiers it covers and the ones it does not, and starting an out-of-scope identifier is leaving the child. A child never merges itself or pushes the default branch; a human reviews it.

The catalog is frozen at the cut. No identifier is appended to a live master; a "we should also" becomes the next parent, under a new prefix. Every child plan restates the child contract in full, because each item in it was already a rule when it was broken. The last todo of every child is the ledger: a row in every area it touched, and a pointer on the master.

## Guards that judge the record

The ledger guard exists because nothing checked that a branch which changed code also wrote down what it did.

- It maps every top-level source directory to the area whose ledger owns it. A directory with no owner fails, so the mapping cannot fall behind the code.
- Against the branch's merge base, each owning ledger needs added lines that carry a date and a known identifier, sit first among the dated rows by position, and cite at least one path or script that resolves.
- Numeric claims, such as a test count, are compared with the measured suite. When the suite cannot run, the guard fails closed instead of accepting a claim it cannot check.

A reachability guard catches a different lie: a module that exists but that nothing runs. Roots come from the package scripts and the service entry, not from a hand-kept list, and the guard walks imports outward from them. Two orphans importing each other pass a naive "somebody imports me" check, though neither can run. The walk rejects that shape.

Known-unwired modules sit on a printed, dated list that fails on any new entry and is only allowed to shrink. No guard may hand-list the files it guards, because a hand-written list silently stops covering the file someone forgot.

## Proving the guards

A guard that passes on broken code is worse than no guard, because it reads as protection. Each guard therefore comes with a mutation: a named edit to one file that reintroduces the defect it claims to catch.

A runner confirms the baseline suite is green. For each mutation it applies the edit, runs the witness tests, restores the file, and records whether the suite went red.

- A green suite after a mutation is reported as not caught.
- A mutation that does not apply fails loudly.
- A new guard without a mutation is not a guard.

```mermaid
%% The mutation loop: every guard is proven by reintroducing the defect it claims to catch.
flowchart TD
  baseline["Baseline green"] --> apply["Apply mutation"]
  apply --> witness["Witness tests"]
  witness -->|"red"| restore["Restore file"]
  witness -->|"green"| missed["Not caught: fix the guard"]
  restore --> manifest["Proof manifest"]
  manifest --> gate["N of M proven"]
```

A full run takes a while, so a failed run resumes instead of restarting. Every proof is recorded with hashes of the mutated file, the mutation's definition, and the tests that witnessed it. A resumed run re-proves any earlier mutation whose recorded proof no longer matches the tree. The last line of every run states how many mutations are proven against this tree, and only all of them is the gate for a pull request.

The runner is a script, not a shell loop, because two hand-rolled loops produced false passes. The check must be unable to produce the outcome it exists to detect.

## Evidence discipline

Orientation governs the truth the repository owns. Evidence governs the truth it does not.

For any third-party API, SDK, platform, protocol, or version, the model's recollection may raise a question but may not answer one. The check happens before the claim reaches a person, the code, or the ledger; being corrected is not the verification step.

- A live probe outranks the vendor's current documentation, which outranks a dated third-party source.
- Where the live system and its documentation disagree, the live system wins.
- Every claim carries what was read, where, and when.
- If a fact cannot be checked, the agent says so and stops.

The same rule turns inward. "It is wired", "I fixed that", and "the guard covers it" are recollections of having written something. Before one reaches a pull request or a ledger row, the agent runs the command that proves it. Wired means naming the caller that is not a test. Fixed means the guard fails when the fix is reverted.

## Incident memory

Area doctrine recorded expensive lessons in full, and the lessons repeated anyway. An agent orienting for one task reads only the doctrine that task points at.

The hazards list is the short list every session reads before it touches anything; it is one of the gate's required reads.

- A row is admitted once its mistake has happened twice or cost more than an hour.
- Each row carries the cost and the mechanical check that now catches it. If no check exists yet, the row says so rather than looking protected.
- A test keeps the list honest: every command it prescribes exists, and every link resolves.

## What it prevents

- A session cannot start before it has read, and it is closed again on every branch change.
- The map cannot silently go stale, because what it says is generated or asserted on every commit.
- Design and history do not blur; neither file can pretend to be the other.
- Scope cannot creep inside a live program, because identifiers freeze at the cut.
- Code cannot ship without a dated, cited record in the right ledger.
- A module cannot be called wired when nothing runs it.
- A guard cannot be vacuous, because each one is proven by reintroducing its defect.
- A test suite cannot reach production. It runs only against local containers on their published ports, with no override.
- A stale external fact cannot be asserted with the fluency of a fresh one.
- A mistake made twice becomes a check, not a memory.

This site is built under a lighter version of the same harness: an orientation protocol, a doctrine and a ledger per area, master and child plans, a ledger guard, and a guard on what the site publishes.
