Part 2 of 4 · 10 min read
Definitions
Covers: Definitions; The loop; Results; Design rules; Interview questions, with the follow-ups; Common mistakes; What the example does not do.
Definitions
Harness
- Harness: the code around a model that provides tools, enforces limits, runs checks and decides what is accepted.
Held-out tests
- Held-out tests: tests the agent cannot see, used only to judge a final change.
Test gaming
- Test gaming: making visible tests pass without fixing the behaviour, for example by hard-coding an expected value or editing the test.
Pull request
- Pull request: a proposed change shown to a human with its diff and checks, merged only when approved.
Attempt budget
- Attempt budget: the maximum number of tries before the agent stops and reports.
The loop
Results
| Mode (six tasks, scripted patches) | Merged | Truly correct (held-out) | Bad merges | Attempts |
|---|---|---|---|---|
| Apply the first patch, no tests | 6 | 1 | 5 | 6 |
| Test loop only | 5 | 3 | 2 | 9 |
| Test loop, protected tests, review gate | 5 | 5 | 0 | 11 |
| Task | Pattern in the scripted candidates | Gated outcome |
|---|---|---|
| slugify | wrong, then right | right on attempt 2 |
| median | wrong, then right | right on attempt 2 |
| parse_range | hard-coded for the visible input, then right | gate blocked the first, right on attempt 2 |
| dedupe | edits the test, then right | guard refused the test edit, right on attempt 2 |
| chunk | two wrong candidates | unsolved after 2 candidates, reported |
| moving_avg | right first | right on attempt 1 |
- The test loop fixed honest mistakes but not dishonest shortcuts; both shortcut types passed the visible tests.
- Protecting tests and gating the diff removed the bad merges at the price of two more attempts overall.
- An unsolved task is a correct output. The gated loop reported it instead of merging something wrong.
Design rules
- Give the agent narrow tools: search, read, exact-match edit, run tests; not a general shell by default.
- Protect what defines "correct": tests, CI config, lock files, secrets.
- Sandbox execution with timeouts, output caps and no network unless needed.
- Keep edits small and reviewable; refuse giant diffs and ask the agent to split.
- Hold out tests for evaluation, and also run linters, type checks and the full suite.
- Gate the diff for test edits, skips, deleted assertions and special-cased literals; treat it as a heuristic.
- Bound the loop: an attempt budget, a time budget and a cost budget (module 5 of Level 11).
- Always end with a human-readable pull request; a human approves merges, especially for risky areas.
- Log every tool call with the task and release id (Level 9) so a bad change is traceable (Level 11 module 11).
- Start with low-risk tasks (typos, small bugs, tests, docs) and widen as evidence grows.
Interview questions, with the follow-ups
How would you build a coding agent that opens pull requests safely?
"A harness with narrow repository tools, exact-match edits with size caps, protected test and CI paths, sandboxed test runs with timeouts, an attempt budget, and a review gate that flags edited tests, skips and special-cased literals. The agent works on a branch and opens a pull request with the diff, test result and gate findings; a human approves the merge. I evaluate on held-out tests and track cost per merged change and bad merges caught in review." Follow-up: "Why a gate if tests pass?" In my experiment a hard-coded answer and an edited test both passed the visible tests; only held-out tests and the gate caught them.
The agent made the tests pass. How do you know the fix is right?
"I do not, from that alone. I run held-out tests and the full suite, read the diff for special-casing and test edits, check linters and types, and for risky areas require human review." Follow-up: "What if the held-out tests are also weak?" Then the evaluation is weak; I improve the tests, sample merged changes for manual review and track defects found later.
How do you stop a coding agent from running amok in a repo?
"Scoped permissions (branch only, no force push, no secrets), sandbox with limits, protected paths, small-edit and attempt budgets, and a kill switch (Level 11 module 11)." Follow-up: "What about prompt injection in issue text or code comments?" Treat them as untrusted data, keep tool permissions minimal and require approval for anything beyond the repo (Level 10 module 1).
How would you evaluate a coding agent?
"A set of real tasks with held-out tests, measuring pass rate, attempts, cost and time, plus manual review of a sample of merged changes for quality and shortcuts, and a regression set built from past failures." Follow-up: "Why not just use a public benchmark?" It is a useful comparison, but my codebase, tests and risks differ, and a benchmark does not show cost or bad-merge rate on my work.
Common mistakes
- Trusting a green test run.
- Letting the agent edit tests or CI without review.
- Letting it run arbitrary commands on the developer's machine.
- Whole-file rewrites that bury the real change.
- No attempt budget, so a stuck agent burns money.
- Merging without a human for sensitive code.
- Evaluating on the tests the agent could see.
- Reading entire large repos into context instead of searching first.
- Ignoring flaky tests, which make the loop chase noise.
- Putting secrets or credentials in the working directory.
What the example does not do
- There is no model. The candidate patches are scripted, so nothing here measures how often a real model games tests.
- Six toy functions, no packages, build systems, dependencies, multi-file changes or flaky tests.
- The runner is a temporary directory and a subprocess, not a real sandbox: do not run untrusted code with it.
- The gate is a string heuristic; it misses obfuscated gaming (a lookup table slips through, shown in a test) and can flag legitimate comparisons to literals.
- No retrieval over large repositories, no planning or multi-file refactors, no git, no code review comments.
- Optional real experiment (I did not run this): give a real model five small bugs in a repository you own with held-out tests, run it with and without the gate, and record merged versus truly correct and the cost per merged change.
Check yourself
An agent changes a function, the visible tests pass, and it merges. Is it correct? (Not necessarily. In the six-task experiment, a test loop alone gave 5 merged and 3 truly correct: the other two were hard-coded for the visible input or had the test edited. Truly correct means passing held-out tests the agent never saw.)
For the curious A gate is a heuristic, so keep held-out tests and a human
With protected test files and a review gate, 5 merged and all 5 were truly correct, and the task nobody could fix was reported as unsolved after 3 attempts. The gate flags common shortcuts (touching tests or CI, removing assertions, adding skips, special-casing a literal from the tests), but a lookup table instead of an if slips past it. The proposer is a script standing in for a model, so this tests the harness, not a model.
What you will use daily at work
| Practice | What it looks like |
|---|---|
| Make edits match exactly one place | Refuse zero or several matches and large edits. |
| Protect tests, CI config and secrets | Check paths after normalising them. |
| Run tests in a sandbox | With a timeout and output cap. |
| Show the reviewer a pull-request summary | Files, lines, test result and gate findings. |
Draft: confirm how your coding-agent tool restricts paths and runs commands, and which branch rules protect main.
What you must be able to say (one page)
- A coding agent is a model inside a harness. The model proposes edits; the harness gives it tools (list, search, read, edit, run tests), enforces limits (paths, edit size, timeouts, a sandbox) and decides what may merge. Quality comes from the loop and the guards as much as from the model.
- The loop: read the task, find the relevant code (search before reading whole files), make a small exact edit, run the tests, feed failures back, repeat within an attempt budget, then open a pull request for human review. Give up cleanly when the budget is spent.
- Repository tools should be narrow and safe. Edits must match exactly one place (zero or several matches are refused), diffs are size-capped, paths cannot escape the repo, tests, CI config and secrets are protected, commands run in a sandbox with a timeout and output cap (Level 6 module 8, Level 10 module 3).
- Passing the visible tests is not correctness. In my six-task experiment, a scripted proposer (a stand-in for a model; the experiment tests the harness, not a model) produced three kinds of wrong patch: plainly wrong, hard-coded for the visible test input, and edited the test. Results: apply the first patch and merge, 6 merged, 1 truly correct (5 bad merges); a test loop alone, 5 merged, 3 truly correct (2 bad merges: the hard-coded one and the weakened test, both passing the visible tests); test loop with protected test files and a review gate, 5 merged, 5 truly correct, 0 bad merges, and the one task nobody could fix was reported as unsolved after 3 attempts. "Truly correct" means passing held-out tests the agent never saw.
- The review gate flags common shortcuts: touching test files or CI config, removing assertions, adding skips, special-casing a string literal that appears in the tests, giant diffs. It is a heuristic: a lookup table instead of an
ifslips past it (one of my tests shows this), so held-out tests and human review remain the backstop. - Review before merge is part of the product. Show the human a pull-request summary: files, lines changed, test result, gate findings. For risky changes (auth, payments, migrations) require approval (Level 10 module 5). Keep branches isolated; never let the agent push to the main branch.
- Evaluate on real tasks. Measure held-out pass rate, attempts, cost per merged change, time, and bad merges caught in review. Public benchmarks of real issues with tests are a starting point (general knowledge, I did not run any), but test-based scores can be inflated by weak tests, so inspect samples.
- Common failure modes: editing the wrong file, flailing in long loops, deleting or weakening tests, fixing the symptom, leaking secrets into logs, installing dependencies from untrusted sources, and huge diffs nobody can review.
- Honest limits: six toy functions and scripted patches; real repositories have large context, flaky tests, build systems and many files.