Coding Agents
A coding agent reads, edits, and runs tests in a repo. Permissions, diffs, and why "it compiles" is not the acceptance test.
- coding-agents
- agents
- devtools
Green tests on a suite the agent deleted are a pass only if you are not looking. Coding agents read, patch, and run commands in a repo. “It compiles” is a compiler’s opinion. Acceptance is the diff, the tests that still exist, and the product checks you did not outsource to the model.
Treat the agent like a junior PR, not like a compiler with vibes. You would not merge a human who removed tests/ and pushed. Do not merge a sampler that did the same with a cheerful summary.
Key takeaways
- The loop is read, edit, run. Chat-only paste is not a coding agent.
- Allowlist paths and commands. The model does not get a login shell.
- Review the diff: scope, secrets, lockfile, snapshots, deleted tests.
tscgreen is not the product. Frontend still needs a11y and visual checks.- Put the test runner in the loop. Do not ask the model whether tests passed.
Coding agents read, edit, and run
A coding agent is a tool loop over a working tree. It lists, greps, reads a slice, applies a patch, runs a pinned test command, and repeats until green or the cap. Products like Claude Code and editor assistants such as GitHub Copilot in VS Code wrap that loop with permissions and UI. Your own version can be a Route Handler plus a sandbox. The shape does not change.
type Patch = { path: string; unifiedDiff: string };
const READ_ROOTS = ['src/', 'tests/', 'package.json', 'tsconfig.json'];
const WRITE_ROOTS = ['src/', 'tests/'];
const TEST_CMD = ['npm', 'test', '--', '--runInBand'];
export async function codingTurn(repo: Repo, goal: string, maxSteps = 12) {
const beforeTests = await repo.list('tests/');
let lastLog = '';
for (let i = 0; i < maxSteps; i++) {
const action = await model.act({ goal, lastLog, tree: await repo.map() });
if (action.type === 'read') {
assertRoot(action.path, READ_ROOTS);
lastLog = await repo.read(action.path, { limitBytes: 8_000 });
continue;
}
if (action.type === 'patch') {
for (const p of action.patches as Patch[]) {
assertRoot(p.path, WRITE_ROOTS);
await repo.apply(p.unifiedDiff);
}
continue;
}
if (action.type === 'test') {
const result = await repo.spawn(TEST_CMD, { timeoutMs: 60_000 });
lastLog = result.stdout.slice(-4_000);
if (result.code !== 0) continue;
const afterTests = await repo.list('tests/');
if (afterTests.length < beforeTests.length) {
throw new Error('tests deleted; refusing green');
}
return { ok: true as const, diff: await repo.diff() };
}
}
return { ok: false as const, diff: await repo.diff(), lastLog };
}
function assertRoot(path: string, roots: string[]) {
const n = path.replace(/^\.\//, '');
if (n.includes('..') || !roots.some((r) => n === r || n.startsWith(r))) {
throw new Error(`blocked ${path}`);
}
}
The tests deleted check is the point of the snippet. Typecheck cannot see a missing test.
Keep the working tree dirty in a branch the agent owns. Do not let it commit until a human (or a signed CI policy) says so. A coding agent that git pushes to main is a deploy tool you did not mean to ship. Diff first, PR second, merge last.
Allowlists beat prompt manners
Tool schema design for coding is least privilege:
read/grep/apply_patch/run_tests, notbash.- If you must have a shell, enumerate
npm test,npx tsc --noEmit,npx eslint. Nocurl, nogit push, norm -rf. - Block
.env,.pem,credentials.json, and anything your secrets guide already treats as radioactive. - Network off by default. Package installs are a human step.
Editor Copilot flows still execute in your workspace. The same rules apply: a suggested terminal command is untrusted text until you run it. HTML the agent writes is untrusted too. If it lands in dangerouslySetInnerHTML, you just invented XSS with extra steps.
Review the diff like a junior PR
The artifact is the patch, not the assistant’s bullet list.
| Check | Why |
|---|---|
| File set | Drive-by refactors and “while I was here” formatting |
| Deleted tests / snapshots | Cheap green |
Lockfile and package.json |
Unexpected deps, scripts, postinstall |
| Secrets and URLs | Keys in source, webhook to somewhere new |
| Generated files | The agent rewrote dist/ instead of src/ |
| Scope vs ticket | Three unrelated components in one PR |
You already know this review from humans. Automate the cheap bits in the inner loop (path allowlist, test-tree diff) and in CI eval gates (lint, typecheck, unit, a11y). Leave taste and product intent for a person.
Frontend is not tsc --noEmit
A React change can typecheck and still trap focus, skip a label, or break a visual regression. Put those oracles in the loop when the diff touches UI:
axe(or your a11y CI) on the affected route- A screenshot test or Chromatic-style check for the component
- Keyboard path: tab order is not a type
The agent should run your commands, not invent npm test -u. Updating snapshots is a write to the truth. Require a human, the same way you would for a golden master.
Interview answer (out loud)
“A coding agent is a read, edit, run loop over a repo with path and command allowlists. I review the diff like a junior PR: deleted tests, secrets, lockfile, extra files. Compiles and green tests are necessary, not sufficient, especially if the suite shrank. For UI I add a11y and visual checks. The model does not get a free-form shell, and it does not get to tell me the tests passed. The runner does.”
Further reading (primary sources)
Related on this site
Related guides
- Harness Engineering for Coding AgentsA harness is the loop, tools, tests, and permissions around a coding model. The model is the easy part. The harness is why some repos get useful diffs.
- Agent Failure ModesAgents loop, lie about tool results, skip confirmation, and get stuck apologizing. Catalog the failures and the circuit breakers.
- Agent HandoffsA handoff passes the conversation to another agent with a slimmer prompt and different tools. What to copy, what to drop, and how to avoid ping-pong.
- Agent MemoryAgents forget when the context window ends. Working memory, session summaries, and long-term stores are three different systems. Do not mix them.
- Agent System Design InterviewDesign a support or coding agent in a loop: tools, approvals, traces, evals, and cost. The frontend surfaces matter as much as the model.