← Home

Harness Engineering — Development in the Age of Agents

Synthesis of three key sources: OpenAI experiment (Ryan Lopopolo), Mitchell Hashimoto’s adoption journey, and Birgitta Böckeler’s analysis (Thoughtworks/Martin Fowler).


Definition

Harness Engineering — the discipline of designing environments, tools, and feedback loops so AI agents do reliable work. Humans steer, agents execute.

“When the agent struggles, we treat it as a signal: identify what is missing — tools, guardrails, documentation — and feed it back into the repository.” — OpenAI

“Harness engineering: anytime an agent makes a mistake, you take the time to engineer a solution such that the agent never makes that mistake again.” — Mitchell Hashimoto


Three Components of Harness (Böckeler/Fowler)

1. Context Engineering — context as code

Continuously improved knowledge base in the repository:

AGENTS.md           <- table of contents (~100 lines)
ARCHITECTURE.md     <- domain and layer map
docs/
├── design-docs/    <- design decisions (with verification status)
├── exec-plans/     <- active and completed plans
│   ├── active/
│   └── completed/
├── product-specs/  <- product specifications
├── references/     <- llms.txt for dependencies
├── QUALITY_SCORE.md
├── RELIABILITY.md
└── SECURITY.md

Key OpenAI insight: One big AGENTS.md is an anti-pattern. Pollutes context, rots instantly, impossible to validate mechanically.

2. Architectural Constraints — boundaries + freedom

Hard boundaries + freedom inside:

“Enforce boundaries centrally, allow autonomy locally.” — OpenAI

3. Garbage Collection — fighting entropy

Agents replicate existing patterns, including bad ones. Without GC, code degrades.

OpenAI: previously 20% of time (Fridays) went to manual cleanup of “AI slop.” Doesn’t scale.


Feedback Loops — the agent’s only ground truth

An agent cannot see. Everything it believes about the running system comes through a loop: a command it runs that returns a verdict. The tightness of that loop sets the ceiling on everything else — context engineering and constraints only decide what the agent tries, the loop decides whether it finds out it was wrong.

A loop is tight when it is:

Build the loop before the work, not after. The pull is always to read code and form a theory first — that’s the failure mode. Whatever the task, name the command that will tell you whether it worked, run it once to see its output, and only then start.

Treat the loop as a product. Once you have one, tighten it: cache setup, skip unrelated init, narrow scope, assert the specific symptom instead of “didn’t crash”. The loop gets used hundreds of times; two seconds saved compounds.

When it can’t be deterministic, raise the reproduction rate instead of chasing a clean repro: loop the trigger 100×, parallelise, add stress, inject sleeps. A 50%-flake is debuggable; 1% is not.

The catalogue of loop constructions — failing test, curl script, CLI+snapshot diff, headless browser, trace replay, throwaway harness, fuzz loop, bisect harness, differential run, HITL script — lives in skills/diagnose/SKILL.md Phase 1, ordered by preference. It’s written for bugs but the constructions are general: any task where you need a verdict picks from the same list.

Loops elsewhere in the factory: TDD’s red→green is a loop with a seam agreed up front (skills/build/references/tdd-seams.md). make integration is the CLI-first loop over business logic. /review is the slow loop over a whole change. /retro~/.solo/evolution.md is the loop over the factory itself. When a stage feels unreliable, ask what its loop is and whether it can go red.


What a loop costs is part of its design

A loop nobody runs guards nothing, and cost is what decides whether it gets run. Four rules, each learned by paying for the opposite:

Split hooks by cost, not by importance. A pre-commit hook that runs the whole test suite takes minutes, so it gets bypassed with --no-verify — and a bypassed hook checks nothing. Put seconds-long tripwires on commit and the suites on push. The tripwires are worth writing by hand: one per bug that actually shipped, phrased as “this file must still contain that guard”.

Measure per test, not per suite. A suite’s total time names the suite, not the culprit. Trimming what looked like the expensive loop in one suite saved three seconds; the real cost was a different test in the same file, and one sort over per-test timings found it in a minute:

<test command> 2>&1 | grep -E "passed|failed" | sort -t'(' -k2 -rn

A test guards a code path, not an amount of data. The same route through the system is exercised by three fixtures or by nine; the extra six buy nothing but wall-clock. Fixture volume is a dial, and its default should be the smallest value that still reaches the path.

Repetition is a deliberate act, not a default. Tests that repeat an operation to catch a flake are worth having and worth switching off: put the count behind an environment variable, default it to one, and raise it before a release or while chasing the flake. Derive the assertions from the fixture too — a hardcoded expectation (“the output is at least 25 seconds”) turns the next deliberate trim red for the wrong reason.

Verify parallelism instead of assuming it. Test runners that parallelise by cloning a whole environment can be slower than serial when the work is not CPU-bound: measured 11m32s against 3m on one iOS suite. And inside a test that measures duration, concurrency destroys the very number being asserted.


6 Steps of Adoption (Mitchell Hashimoto)

Step 1: Drop the chatbot

Chat interface (ChatGPT, Gemini web) is a dead end for serious development. Use an agent: LLM that reads files, runs programs, makes HTTP requests.

Step 2: Reproduce your own work

Do a task manually, then make the agent do the same with the same quality. Painful, but builds expertise:

Negative space value: understanding when not to use the agent saves the most time.

Step 3: End-of-day agents

Block 30 minutes at end of day for agent runs. Don’t try to do more during work hours. Do more in off hours.

What works:

Step 4: Outsource slam dunks

Tasks where agent almost certainly succeeds: let it run in background. Turn off desktop notifications. Human decides when to context-switch.

“Turn off agent desktop notifications. Context switching is expensive.”

Step 5: Engineer the Harness

Every agent mistake -> engineering solution so it never happens again. Two mechanisms:

  1. AGENTS.md / CLAUDE.md — for simple problems (wrong commands, wrong APIs)
  2. Programmatic tools — scripts, screenshots, filtered tests

“Each line in that file is based on a bad agent behavior, and it almost completely resolved them all.”

Step 6: Always have an agent running

Goal: agent always running. If not, ask: “what could the agent be doing for me?”

Preference: slow, thoughtful models (Amp deep mode / GPT-5.2-Codex). 30+ min per task, but high quality. One agent, not parallel.


OpenAI Experiment: Numbers

Metric Value
Engineers 3 -> 7
Duration 5 months
Code ~1M lines
Pull requests ~1,500
PR/engineer/day 3.5
Human code 0 lines
Estimated speedup ~10x
Max single Codex run 6+ hours

Autonomy Levels (achieved)

One prompt -> agent can:

  1. Validate codebase state
  2. Reproduce bug
  3. Record demo video
  4. Implement fix
  5. Validate fix via UI
  6. Record second video
  7. Open PR
  8. Respond to feedback (agent and human)
  9. Detect and fix build failures
  10. Escalate to human only when judgment needed
  11. Merge

Tools for Legibility


Practical Recommendations

  1. CLAUDE.md as table of contents — keep ~100 lines with links deeper
  2. docs/ as system of record — design docs, execution plans, quality scores
  3. Custom linters with agent-friendly messages — remediation instructions right in the error
  4. Structural tests — check dependency direction, file sizes
  5. Doc-gardening — periodic agent for cleaning stale docs
  6. End-of-day agents — issue triage, research, background tasks
  7. Harness per project — each project = its own CLAUDE.md + docs/ + linters
  8. “Boring” tech — prefer stable, composable technologies

Harness Health Checklist

Anti-Patterns


Predictions (Böckeler/Fowler)

  1. Harness as new service templates — organizations will create harness templates for main stacks
  2. Tech stack convergence — AI pushes toward fewer stacks, “AI-friendliness” as selection criterion
  3. Topology convergence — project structures will become more standard (stable data shapes, modular boundaries)
  4. Two worlds — greenfield with harness vs. retrofit on legacy (different approaches)

Further Reading

Curated from awesome-harness-engineering. Grouped by actionability for solo-factory workflow.

Context & Memory (improve pipeline context efficiency)

Long-Running Agents (improve /build, /pipeline)

Agent Design Patterns (audit skills against)

Evals & Quality (improve /retro, /skill-audit, /review)

Safe Autonomy (improve sandboxing, guardrails)

Multi-Agent (improve agent-teams, /swarm)


Sources:

Sources

Related