Harness engineering: guides and sensors
A coding agent is a model plus everything around it. You can't change the model, but you can change everything around it: what steers the agent before it writes, and what catches its mistakes after. Thoughtworks calls this the agent's harness. This module shows how to build one that makes the agent correct itself, and how to improve it every time a mistake repeats. It works the same way in any coding agent.
- Sam What's "harness engineering"? It sounds like something for horses.
- Priya It's everything around a coding agent except the model itself. You can't change the model, but you can change what steers it before it writes, and what catches its mistakes after. Build that well and the agent fixes most of its own mistakes before you ever look.
- Priya Think of a new cook in a restaurant kitchen. Recipe cards steer them before they cook. A thermometer and the head chef tasting each plate catch problems after. And when the same mistake happens twice, a good head chef doesn't shout louder: they fix the recipe card or add a check.
- Sam So two kinds of control.
- Priya Guides, which steer before the agent acts: AGENTS.md, skills, specs, templates. And sensors, which observe after: the compiler, the linter, tests, structural tests, AI review. Each can be computational, meaning deterministic code, fast and cheap, or inferential, meaning another model's judgement, slower but it understands meaning. You need both. With only sensors, the agent keeps repeating mistakes. With only guides, nobody checks the rules were followed.
- Sam How does it fit together?
- Priya The agent reads the guides and makes an edit. Fast sensors run straight away: types, lint, the quick tests. If one fails, its message goes back to the agent, ideally saying how to fix it, and the agent corrects itself before committing. Slower sensors, like mutation testing and AI review, run in CI. And you sit on the loop: when the same problem comes back, you fix the harness.
- Sam That's the flywheel?
- Priya Yes. Say the agent keeps using a time function with no timezone. You comment once, twice, three times, because each session starts fresh and remembers nothing. So you add a lint rule whose message says "use our clock function, it returns UTC", plus one line in AGENTS.md. Next time, the sensor catches it in seconds and the agent fixes it itself.
- Priya Your turn. Agent-written tests show ninety-five percent coverage. Are they good?
- Priya Not necessarily. They can pass whatever the code does. Mutation testing makes small deliberate bugs and checks that a test fails for each one. If the bugs survive, the tests are weak.
- Sam What can I try on the page?
- Priya Two sorting exercises. Match each recurring problem to the control that fixes it best. Then trim a bloated AGENTS.md: keep each line, turn it into a sensor, move it into a skill, or delete it.
- Sam Where does the industry stand?
- Priya Thoughtworks' April Radar made it a theme: putting coding agents on a leash. Curated shared instructions are in Adopt. Feedback sensors and mutation testing are in Trial. The feedback flywheel is in Assess. And agent instruction bloat is in Caution: instruction files grow until rules conflict and the important ones get lost.
- Sam How do I set it up for any agent?
- Priya One check script with types, lint and fast tests. One line in AGENTS.md telling the agent to run it before saying it's done. A git pre-commit hook that runs it, which works with every tool. Each tool's own hook to run it after every edit. And CI runs it again, plus mutation testing and AI review.
- Sam Any caveats?
- Priya Sensors only check what can be checked; whether the code does the right thing still needs people and good tests. And re-check the harness when you change models: a rule that helped one model may be noise for the next.
- Sam Does it differ by tool?
- Priya The guides are portable: every tool reads AGENTS.md and the same skills format. Sensors inside the session use each tool's hooks: a post-edit hook in Claude Code, Codex and Cursor, agent-scoped hooks in Copilot, and hooks in Antigravity. The git hook and CI cover all of them.
- Sam The memory hook?
- Priya Recipe card before, taste test after. And when a mistake repeats, fix the kitchen, not the cook.
- Sam Common mistakes?
- Priya Rules with no checks. Sensor messages written for people instead of the agent. Running every sensor only in CI. A bloated AGENTS.md. Trusting coverage. Letting the agent edit its own sensors. And never pruning old rules.
- Sam And the lab?
- Priya List three review comments you've made more than once. Write a check script, turn one repeat into a lint rule with a helpful message, and watch the agent fix it itself. Run mutation testing on one module, and trim your AGENTS.md.
1. The idea
Birgitta Böckeler (Thoughtworks) defines the harness as "everything in an AI agent except the model itself". The coding agent comes with a built-in harness (its tools, its loop, its prompts). Harness engineering is the part you add for your codebase, and it has two kinds of control:
- Guides (feedforward) steer the agent before it acts, so the first attempt is more likely to be right.
- Sensors (feedback) observe after it acts, so the agent can correct itself before a person looks.
Each can be computational (deterministic code: fast, cheap, always gives the same answer) or inferential (another model's judgement: slower, costs tokens, can vary, but understands meaning).
You need both kinds. Böckeler's point: with only sensors, the agent keeps repeating the same mistakes; with only guides, you write rules and never find out whether they were followed.
2. How it works
The last step is what makes a harness get better over time. Thoughtworks calls it the feedback flywheel: treat recurring agent mistakes like a retrospective, and fix the harness instead of correcting the agent by hand again. Follow one recurring mistake:
Agent-written tests have their own trap: they can be "perpetually green", passing whatever the code does because they assert too little. Coverage can't see this; mutation testing can. It makes small deliberate bugs ("mutants") in the code and checks that some test fails for each one.
domain/ports/ and inject it (see docs/architecture.md)." Böckeler calls these messages "optimised for LLM consumption". The better the message, the fewer loops the agent needs.3. Try it yourself
Which control fixes this?
Each line is a problem that keeps happening. Pick the control that fixes it best, then check.
Trim this AGENTS.md
This file grew for a year and now the agent ignores half of it. For each line: keep it, turn it into a sensor (a check that enforces it), move it into a skill (loaded only when relevant), or delete it.
4. Why it matters at work
Thoughtworks' April 2026 Technology Radar made this its theme "Putting coding agents on a leash", warning that as agents improve, people are tempted to step out of the loop. Where the related practices stand:
| Practice | Radar ring | What it means |
|---|---|---|
| Curated shared instructions | Adopt | A team-maintained AGENTS.md and shared skills, reviewed like code. |
| Feedback sensors for coding agents | Trial | Wire compilers, linters, structural tests and test suites into the agent's workflow so failures trigger corrections, ideally during the session, before a commit. |
| Mutation testing | Trial | A check on weak, "perpetually green" tests; tools such as Stryker, Pitest and cargo-mutants. |
| Progressive context disclosure | Trial | Load only the instructions the current task needs (skills do this). |
| Feedback flywheel | Assess | Capture what went well and badly in agent sessions and improve the harness; re-check it when you change models. |
| Agent instruction bloat | Caution | Instruction files that grow until rules conflict and important ones get missed. |
Where each sensor runs
Fast sensors go as early as possible; slow ones later. Böckeler's lifecycle:
| When | Sensors | Why there |
|---|---|---|
| After every edit, in the session | Types, lint, formatter, the fast unit tests | Seconds; the agent fixes it while it still has the context. |
| Before a commit | All unit tests, structural tests | Nothing broken leaves the agent's session. |
| In CI, after integration | Mutation testing, AI review, security scans, slower tests | Minutes, costs more; catches what fast checks can't. |
| Continuously | Architecture drift checks, production SLOs | Catches slow decay and problems only real traffic shows. |
The portable setup
One check script, called from everywhere. Any agent can run it, a git hook runs it whoever commits, and CI runs it again.
#!/usr/bin/env bash
# scripts/check: the fast sensors, in one place. Exit non-zero on any failure.
set -euo pipefail
npx tsc --noEmit # types
npx eslint . --max-warnings 0 # lint, including architecture rules
npx vitest run --changed # tests for what changed
# AGENTS.md: the guide that points at the sensor (short, so it isn't lost)
Before you say a task is done, run ./scripts/check and fix every failure.
Never edit eslint.config.js, the tests' assertions or CI files to make a check pass.
// eslint.config.js: a sensor whose message tells the agent how to fix it
rules: {
"no-restricted-imports": ["error", { patterns: [{
group: ["**/infra/**"],
message: "Domain code can't import infra. Depend on the port in domain/ports/ and inject it (see docs/architecture.md#ports).",
}] }],
}
- Git pre-commit hook runs
scripts/check: works with all five tools, because it runs whoever (or whatever) commits. - The agent's own hook (Module 12) runs the fast part after every edit, so mistakes are fixed in the session. Where each tool keeps it is in section 5.
- CI runs
scripts/checkagain, plus mutation testing on core logic and the AI review from Module 13a.
5. In your five tools
Guides are portable (AGENTS.md and the SKILL.md format work everywhere); sensors in the session use each tool's hooks. Checked October 2026; see Modules 09, 11 and 12 for details.
| Tool | Guides: instructions and skills | Sensors in the session: run the check after edits |
|---|---|---|
| Claude Code | CLAUDE.md (reads AGENTS.md if there's no CLAUDE.md, or @AGENTS.md); skills in .claude/skills/ | A PostToolUse hook in .claude/settings.json on Edit and Write; exit code 2 sends the failure back to the agent |
| Antigravity CLI | AGENTS.md or GEMINI.md; .agents/rules/; skills in .agents/skills/ | Hooks via /hooks |
| GitHub Copilot | .github/copilot-instructions.md, AGENTS.md, .github/instructions/; skills in .github/skills/ or .agents/skills/ | Agent-scoped hooks in a custom agent's frontmatter; hooks in the cloud agent |
| Cursor | AGENTS.md or .cursor/rules/*.mdc; skills in .agents/skills/ | .cursor/hooks.json |
| Codex CLI | AGENTS.md (merged down the folder tree); skills in .agents/skills/ | .codex/hooks.json or [hooks] in config.toml: PostToolUse |
The git pre-commit hook and CI catch everything the in-session hooks miss, for every tool.
6. Remember it
How this hook works: what “Recipe card before, taste test after” means
What the slogan means, phrase by phrase
- “Recipe card before”
- Guides: AGENTS.md, skills, specs, templates. They make the first attempt more likely to be right.
- “taste test after”
- Sensors: types, lint, tests, structural tests, mutation testing, AI review. They let the agent correct itself.
Why this picture fits: no kitchen relies only on recipes or only on tasting. And a head chef who fixes the recipe card once saves correcting every cook every night.
The story → the concept
| Kitchen | Harness |
|---|---|
| Recipe cards | AGENTS.md, skills, specs (inferential guides) |
| Station layout, pre-portioned ingredients | Templates and codemods (computational guides) |
| Oven thermometer | Compiler, linter, tests (computational sensors) |
| Head chef tasting | AI review (inferential sensor) |
| Fixing the recipe card | The feedback flywheel |
The trap it saves you from
Correcting the same agent mistake by hand, session after session. If you've typed the same review comment twice, it belongs in the harness.
7. Common mistakes
8. Hands-on lab (roadmap week 12)
- Collect the repeats: list three review comments you've made on agent code more than once.
- Write the check script: types, lint and fast tests in
scripts/check; point AGENTS.md at it in one line. - Turn one repeat into a sensor: a lint rule or structural test whose message says how to fix it. Ask an agent to make the mistake on purpose and watch it correct itself.
- Wire it in: a git pre-commit hook for every tool, plus the after-edit hook in tool A.
- Run mutation testing (Stryker, Pitest, mutmut or cargo-mutants) on one core module the agent wrote tests for. Add tests until the important mutants are killed.
- Trim AGENTS.md: remove or move every line that a sensor now enforces or only one task needs. Compare its length before and after.
9. Self-check quiz
A linter that fails when domain code imports infrastructure code is…
You've made the same review comment on agent code three times. Best fix?
Agent-written tests show 95% coverage but you suspect they assert nothing useful. What reveals it?
Which sensors belong inside the agent's session?
What's the risk of an AGENTS.md that keeps growing?
Your team switches to a newer model. What should happen to the harness?
10. Go deeper
| Resource | Time | Why |
|---|---|---|
| Harness engineering for coding agent users (Birgitta Böckeler, martinfowler.com, April 2026) | 25 min | The source of this module's model: guides and sensors, computational and inferential, and where each runs. |
| Thoughtworks Radar, April 2026: Feedback sensors, Feedback flywheel, Agent instruction bloat, Mutation testing | 10 min | One paragraph each on what teams found in practice. |
| Stryker, Pitest, mutmut, cargo-mutants | 15 min | Mutation testing for JavaScript/TypeScript/C#, Java, Python and Rust. |
| ArchUnit and import-linter | 15 min | Structural tests: architecture rules as sensors, for Java and Python. |
| Modules 09, 11, 12 and 13a | – | Instructions, skills, hooks and AI review: the parts this module joins up. |
| Case study C05: Replit's agent deletes production | Case | Why instructions failed, and the guards and environments that work. |
| Case study C06: a GitHub issue hijacks an agent | Case | Prompt injection through a tool, and the policies that stop the leak. |