Skip to main content
Interlaced eucalypt canopies with birds threading between the crowns, in a three-ink risograph print

Backpressure

Most of harness engineering is giving good negative feedback: the model generates, and the harness rejects what's wrong, fast and automatically

A coding agent will generate for as long as you let it. Generation is the cheap part: the model supplies as much plausible-looking code as you can pay for. What’s scarce is automatic rejection. Backpressure is the set of checks that reject bad output, and configuring it is most of harness engineering.

That observation comes from Simon Willison, who states it as a principle of its own: writing code is cheap now. Generating code is no longer the expensive part, so practices designed around scarce generation need revision.

“Negative feedback” is meant here in its control-theory sense: the error-correcting loop that stabilises a system, which is cybernetics in its original meaning (steering). It’s also meant in the everyday sense, the critique you give and receive at the crit. A good failing test and a good crit comment must both be specific, early, and aimed at the artefact rather than the author.

Guides and sensors

The corrective signal runs through two kinds of channel (vocabulary borrowed in part from Learn Harness Engineering; Birgitta Böckeler splits it the same way):

This topic is mostly about sensors; the big guides get their own treatment (CLAUDE.md in week 2, skills in week 4). Use each sensor to answer one question: does the artefact meet the requirement? Testing and linting are the everyday instances, but any automatic comparison between the artefact and its requirements can provide this feedback. A useful sensor is:

Once configured, sensors can run on every change at negligible marginal cost. Your attention is the scarcest resource in the loop, so automating a correction saves you from repeating the same check.

Four sensors worth wiring in

The rest of this page covers four concrete sensors, from code checks to the rendered page itself.

Linting and types

Lint output gives an agent a machine-readable correction: a rule name and a location. The agent can act on that output in the next turn without further instruction or interrupting a human.

Type checkers catch errors with greater consequences. Agents often generate plausible code that reads correctly but is wrong; a type checker converts many of those errors into diagnostics with exact locations before anything runs. Agentic practice has therefore strengthened the case for types and strict lint configs: every enabled rule automates another correction.

The sharpest sensor is a failing test

Lint and types reject malformed code. A test can also reject code whose behaviour differs from the expected result. A failing test is backpressure you author yourself: it encodes your intent as a check the harness can run and gives the agent a specific failure to fix.

The resulting habit is short enough to keep: only push on green. This section does not require the full test-driven ceremony. Red-green-refactor is a fine discipline, but here we are concentrating on the feedback channel. Write a test when you know something the code should do, and keep the suite green as the gate on every push.

stylelint: grounding for CSS

Agents produce particularly unreliable CSS. The cascade has no type checker, a selector that matches nothing raises no exception, and the model’s training data contains plenty of copied rules with conflicting declarations. An agent can therefore produce plausible CSS while few automated checks detect the visual failure. stylelint reports invalid values, duplicate or conflicting declarations, and selectors that cannot match. It cannot judge the design, but it prevents invalid and contradictory CSS from accumulating.

agent-browser: the rendered page as ground truth

Everything above checks the code. None of it shows the agent what the page looks like. Without rendered output, an agent may report that the layout is fixed because the CSS it wrote appears correct. Vercel’s agent-browser CLI closes that loop: the agent drives a real browser, takes screenshots, reads the accessibility tree and the console errors, and compares what actually rendered against what it believes it built. A contradictory screenshot provides evidence that the claim was wrong.

Used defensively like that, it is a sensor. Used deliberately — driving the artefact the way a user would, to find what nothing else reaches — it is a named practice called agentic manual testing, which is week 5’s business.

On Ubuntu 23.10 and later the browser won’t start under the defaults: AppArmor restricts the unprivileged user namespaces Chrome’s sandbox needs, and the run exits with No usable sandbox! before any page loads. Pass --args "--no-sandbox" on whichever command launches the browser:

agent-browser --args "--no-sandbox" open http://localhost:4321/

That turns off Chrome’s own process isolation, which is a fair trade for a browser pointed at your own dev server — just don’t reuse that session for general web browsing.

The roster, wired into CI

Your starter repo runs these sensors through one pnpm check: the type checker, oxlint, stylelint, the build, and a test suite that includes the invariant checks. The crit 7 starter adds axe-core to those invariants, an accessibility floor run in jsdom; the final project’s template ships only two checks, that the app answers and that /readme/ publishes your README, and leaves the rest of the roster to you. While your repo is private they run locally via the starter’s scripts (the faster loop anyway); from the moment it goes public at your cutoff, CI runs them on every push. The repo’s CLAUDE.md describes each check and how to interpret a failure. Two habits complete the loop:

The task list is another sensor: an external record for checking the agent’s completion reports.

When no sensor catches it

Some changes are wrong in ways no sensor catches until too late, and then the question is which correction to reach for. That judgement is mostly about how early you are. A wrong direction caught mid-response is cheapest to interrupt; a few turns later you may need to rewind the conversation; once the change has reached the repository, use git revert. The cheatsheet has the mechanics, including an important boundary: Claude Code’s checkpoints cover the conversation and its own edits. Use git history after any other tool has changed your files.

All of that depends on knowing what happened rather than what the agent said happened. Use the diff, check output and rendered page to verify any claim of “done”. Because git revert is cheap, you can let the agent work in bounded stretches without watching every turn.

One signal, three channels

This page concentrates on the automated channel: corrections that run without anyone in the loop. You provide another channel by reading the diff, asking the agent to explain itself and checking the task list. Your peers provide a third at the weekly crit. At the crit you practise giving specific feedback; during the rest of the week you automate as much of it as possible.