The Feedback Loop
An agent that can run the tests, the type checker and the linter itself fixes most of its own mistakes before you see them. The speed of those checks sets the speed of the loop. A two-second targeted run gets used after every edit; a three-minute suite gets skipped, or run once at the end, when its failures are hardest to act on.
The previous topics made the tests a contract. This one makes the contract cheap to consult, so the agent consults it constantly and you receive a diff that has already met it.
Checks It Can Run
Chapter 3 gave the agent one command each to build or type-check, test, lint and format, and set approval so those commands run without asking. Both halves matter. A check the agent cannot run is a check only you run, after the fact, on a diff that is already finished. A check that needs your approval every time gets run less often than it should, because each run is an interruption.
The cost of not setting this up is easy to see in a session transcript. An agent that cannot run the type checker writes a change, reports it done, and you run the check and paste the error back. Each round trip is a message, a context that grows, and a few minutes of your attention. An agent that can run the check itself absorbs those rounds in its own loop, where they cost seconds and nobody waits.
Fast and Targeted
Cragmoor's full suite is about 900 tests and takes about three minutes. The refund tests alone run in about two seconds, a local figure. During iteration the agent runs the refund test file after every edit; before it declares the task done, it runs the full suite once. The project instructions file says which command is which and when to use each, because an agent left to choose will either run everything every time or nothing until the end.
The split is not only about time. The targeted run is also a statement of scope: these are the tests the change is supposed to affect. If a change to the refund window breaks a test in booking, the targeted run will not see it and the full run will. So the full run comes before the agent says it is done, and again before you commit.
The targeted run catches most mistakes at a ninetieth of the cost. Over a session of twenty edits, that is the difference between forty seconds of waiting and an hour.
| Check | How long | When |
|---|---|---|
| Formatter | milliseconds | on every save, so stray whitespace never reaches a diff |
| Type checker and linter | seconds | after every edit; they catch what no test reaches |
| Targeted tests: the refund test file | about two seconds | after every edit |
| Full test suite: about 900 tests | about three minutes | once, before the agent says it is done |
Free Reviewers
Each fast check removes a class of finding from your review before it reaches you. The type checker catches invented methods, Chapter 7's loudest failure, in TypeScript and Java, and in Python once one is configured. The linter catches unused imports, shadowed names and the deprecated calls of Chapter 7's last topic, if its deprecation rules are on. The formatter keeps whitespace churn out of the diff, but not the re-indented block that hid the boundary flip in Chapter 6: that indent comes from new nesting, which no formatter undoes.
None of these replace reading. They change what reading is for: once the mechanical findings are gone, your attention goes to the questions only a person can answer.
Each of these checks also has a strictness setting, and the default is usually lenient. A type checker that allows untyped functions skips most of the code an agent writes in a hurry. A linter with only style rules enabled finds no bugs. Turn on the rules that find defects, not only the ones that find formatting, for every module an agent works in.
Output the Agent Can Use
A failing run that prints 3,000 lines, the size of Cragmoor's full suite in verbose mode, fills the context window and buries the one line that matters. Chapter 4 showed what a crowded context does: early instructions fade and the agent starts working from a summary of a summary. While iterating, show failures only, stop at the first failure, and keep the trace for that failure.
tests/test_notice.py::test_notice_matches_deadline PASSED [ 4%] tests/test_notice.py::test_notice_says_21_00_the_evening_before FAILED [ 9%] tests/test_refund_edges.py::test_deadline_is_21_00_local_in_each_zone[gym0] PASSED [ 14%] tests/test_refund_edges.py::test_deadline_is_21_00_local_in_each_zone[gym1] FAILED [ 19%] tests/test_refund_edges.py::test_twelve_elapsed_hours_when_clocks_go_forward FAILED [ 23%] ...
.F
=================================== FAILURES ===================================
E AssertionError: assert 'Free cancell...6-11-13 20:00' == 'Free cancell...6-11-13 21:00'
- Free cancellation until 2026-11-13 21:00
? ^
+ Free cancellation until 2026-11-13 20:00
? ^
tests/test_notice.py:22: AssertionError: assert 'Free cancell...6-11-13 20:00' == 'Free cancell...6-11-13 21:00'
=========================== short test summary info ============================
FAILED tests/test_notice.py::test_notice_says_21_00_the_evening_before - Asse...
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed in 0.03s × notice says 21:00 the evening before 4ms
× deadline shown at second-zone gym 4ms
× deadline is 21:00 local at west 4ms
× 721 minutes ahead refunds 2400 1ms
× 720 minutes ahead refunds 2400 0ms
...
FAIL tests/refund-edges.test.ts > deadline is 21:00 local at west
AssertionError: expected 'Free cancellation until 2026-11-13 20…' to be 'Free cancellation until 2026-11-13 21…' // Object.is equality
Expected: "Free cancellation until 2026-11-13 21:00"
Received: "Free cancellation until 2026-11-13 20:00"
❯ tests/refund-edges.test.ts:20:39
Test Files 1 failed (4)
Tests 1 failed | 1 passed (11)Test plan execution started. Number of static tests: 13 ╷ ├─ JUnit Platform Suite └─ JUnit Platform Suite finished after 3 ms. ├─ JUnit Jupiter │ ├─ RefundEdgesTest │ │ ├─ refundedExactlyWhenAtLeast720MinutesAhead() │ │ │ tags: [] ...
Failures (1):
JUnit Jupiter:RefundEdgesTest:refundedExactlyWhenAtLeast720MinutesAhead()
=> org.opentest4j.AssertionFailedError: 720 ==> expected: <2400> but was: <0>
org.cragmoor.RefundEdgesTest.refundedExactlyWhenAtLeast720MinutesAhead(RefundEdgesTest.java:61)
Test execution was cancelled due to --fail-fast mode.The quiet run is a few lines and says exactly what is wrong: one expected value, one actual value, one file and line. The agent can act on it at once. The verbose run is the same information, plus every passing test, every parameter and every timing, and the agent has to find the failure in it before it can act.
The agent's context is the same budget Chapter 4 described, and test output spends it fast. A verbose failure from Cragmoor's full suite is thousands of lines, most of them names of tests that passed. The agent reads all of it, and its next attempt is written with the task and your instructions further away than they were. Quiet output keeps them close.
The Checks Are Code It Can Change
An agent blocked by a lint rule can add a comment that suppresses it, or edit the linter's configuration to switch the rule off. A type error can be silenced with a cast. A slow test can be excluded from the targeted command. Each of these makes the check pass in the same way a bent test does in Chapter 7: by changing the check, not the code.
So the review that reads every changed test also reads every suppression comment, every cast added near an error, and every change to a check's configuration file. A diff that touches the linter's configuration in a task about refund windows needs a reason before anything else in it is read.
Checks That Run on Their Own
As of 2026, many assistants can run a command automatically after every edit, or before they finish a task; each tool names and configures the setting its own way. A formatter and a fast type check placed there turn the project's rules into reflexes: every edit is formatted and type-checked before the agent's next step. A slow check placed there turns every edit into a wait, and the agent's loop slows to the speed of its slowest hook.
- "Put every check in the after-edit hook, to be safe." A hook runs on every edit, so a slow check there makes every edit wait for it. Put the formatter and a fast type check in the hook, and leave the full suite for the end of the step.
- "If the agent ran the tests, I don't need to." You need the output, not the claim, and you run the full suite before committing, because the agent's runs were targeted.
- "The type checker is optional in Python, so it adds little." It is the check that catches an invented method before the line runs. A dynamic language needs it more with an agent, not less.
- "Lint warnings are about style." Deprecation, unused-variable and unreachable-code rules are bug detectors that run in milliseconds.
- "More output helps the agent debug." Beyond the failure and its trace, extra output pushes the task out of the context.
- Give the agent a targeted test command and a full one, and say in the project instructions file when to use each.
- Turn on the type checker and the deprecation rules for every module an agent touches. They catch invented and outdated calls before review.
- Configure test output to show failures only while iterating. The context is for the task, not the log.
- Review every suppression comment and every change to a check's configuration as you would a changed test. It is the same move, one layer over.
Knowledge Check
The agent is about to make its eighth edit to the refund code. Which check should it run after the edit?
- The full suite of about 900 tests, so nothing anywhere is missed
- No tests at all until it has finished every edit it has planned
- The refund test file and the type checker, in a few seconds
- Only the formatter, since behaviour is checked when it finishes
What does a type checker catch in an agent's change that a test may not?
- A cancellation at exactly 12 hours that is wrongly refused a refund
- An invented method on a branch that no test happens to execute
- A refund share rounded to the nearest cent instead of rounded down
- A deadline computed in the server's time zone instead of the gym's
Why does verbose test output make the agent's next attempt worse?
- Verbose mode runs a different and slower set of tests than quiet mode
- The agent cannot read output longer than a few hundred characters
- Verbose output leaves out the expected and actual values entirely
- It fills the context and buries the one failure the agent must fix
An agent's diff for the refund task adds one line to the linter's configuration file. What should the review do with it?
- Accept it, since configuration files are not part of the product code
- Treat it like a changed test and demand a reason before reading on
- Run the linter once more and accept the change if the linter passes
- Ask the agent to explain the change, then accept what it says about it
You got correct