Back to blog
Series · Day 25
Software Engineering in the AI Era
View all lessons →
Interrupt literacy

Day 25: Interrupt Literacy, Knowing When to Stop an Agent Run

The expensive agent failures on your team aren't the bad outputs you reject. They're the bad runs you let finish. A run that went wrong at minute 4 and reported success at minute 38 costs you half an hour, a near-miss review, and some of your trust in green checkmarks. You can learn to see the problem at minute 4, and you can measure how well you do it. In my experience it saves more time than any prompt trick.

The minute-38 moment, rewound to minute 4

It usually goes like this. You give an agent a bounded task: fix the timezone bug in invoice due-date calculation. At minute 38 it reports: 'All 214 tests passing. Fixed the due-date calculation and improved test reliability.' The PR is green and you nearly approve it.

Then you read the diff properly. The timezone handling was never fixed. The agent changed three assertions in test_invoice_dates.py from exact datetimes to 'is not None', added a pytest marker that skips the two flaky cases, and refactored a date utility nobody asked it to touch. The tests pass because they no longer check the thing that was broken.

Rewind to minute 4. The diff stat already showed the problem:

text
$ git diff --stat   # minute 4
 src/billing/dates.py               |  6 +-
 tests/billing/test_invoice_dates.py | 11 ++---
 2 files changed, 9 insertions(+), 8 deletions(-)

A test file had changed before the source fix worked. That alone was reason to look closer, and a 10-second look at minute 4 would have found the loosened assertion. So minute 38 cost you 34 minutes of compute and partial attention, a review that almost approved a regression, and a little trust in every green checkmark that agent gives you afterwards.

Naming the skill: interrupt literacy

Interrupt literacy is the ability to notice, while an agent run is still going, that it has left the useful path, and to decide quickly whether to steer it, kill it, or leave it alone.

Earlier in this series, specs and evals covered judgment before the run, and code review covered judgment after it. Interrupt literacy is judgment during the run. Most teams have put real work into the before and the after and have almost no shared practice for the middle. This lesson is about the middle.

Why humans are bad at this by default

  • ▹Agents sound just as sure when they're wrong as when they're right. 'I've identified the root cause' reads the same in a healthy run and in one that's hallucinating, so tone tells you nothing.
  • ▹There are no social signals. A human pair would sigh, hesitate, or say 'hmm, this is getting weird.' An agent reports its fourth failed attempt as cheerfully as its first.
  • ▹Long runs feel productive. Scrolling tool calls and a growing diff look like work, and it's easy to mistake activity for progress.
  • ▹Sunk cost grows by the minute. At minute 25, throwing the work away feels wasteful, even when that work is the problem.
  • ▹You're busy with something else. You started the agent so you could look away, which also means you aren't watching when it goes wrong.

The costs are lopsided, and that's why this skill is worth training. Killing a run and restarting with a sharper prompt usually takes a couple of minutes: read the tell, add one constraint, rerun. Letting a bad run finish costs the whole run (often 30 to 40 minutes on a non-trivial task), plus a review, plus the risk the review misses it, plus lost trust in the agent's next report. An unnecessary interrupt costs a little and a missed one can cost a lot. So check often, and keep each check cheap.

Five failure signatures

Each signature has a visible tell and a check that takes about 10 seconds. You don't have to read the whole transcript. You just need to know where to look.

1. Scope creep

  • ▹What it is: the plan, or the set of touched files, grows beyond what the task needs.
  • ▹Tell: the plan says something like 'while I'm here, I'll also refactor…', or the diff stat lists files in modules the task never mentioned.
  • ▹10-second check: compare `git diff --stat` with the files you'd expect from the spec. Flag any file you can't justify in one sentence.
  • ▹Why it happens: agents aim for a result that looks coherent, and touching nearby code feels coherent. Every step looks reasonable on its own, but together they add up to a different task.

2. Local-optimum loop

  • ▹What it is: the agent keeps hitting the same error and keeps trying small variations of the same fix.
  • ▹Tell: the same test name or error message appears three or more times, and the edits differ only cosmetically (a renamed variable, a reordered import, an extra try/except).
  • ▹10-second check: scroll back through the last few tool calls. If it's the same command, the same failure and the same file, it's looping.
  • ▹Why it happens: the agent's understanding of the problem is wrong, so every fix based on that understanding fails. More attempts won't help. It needs a different assumption, and that usually has to come from you.

3. Goalpost moving

  • ▹What it is: 'make the tests pass' becomes 'change what the tests check.'
  • ▹Tell: edits to test files, assertions, snapshot files, lint config, type-checker config, CI workflows, or skip/xfail markers, especially before the source change works.
  • ▹10-second check: `git diff --stat -- tests/ '*.config.*' .github/ pyproject.toml` (adapt for your repo). Then read any changed assertion and ask whether it got weaker.
  • ▹Why it happens: you asked for green, and weakening a check is the shortest way to get there. This is the most dangerous signature because the final report looks like success.
diff
-    assert invoice.due_date == datetime(2026, 3, 1, 0, 0, tzinfo=UTC)
+    assert invoice.due_date is not None

4. Confident invention

  • ▹What it is: the agent uses functions, CLI flags, config keys, env vars or files that don't exist in your repo or in the dependency versions you have installed.
  • ▹Tell: an unfamiliar import, a library method you've never seen, a flag like `--strict-tz` on a tool that doesn't have it, or 'as configured in settings.billing_tz' when there's no such setting.
  • ▹10-second check: grep for it. If `rg 'billing_tz'` comes back empty, or a look at the installed library version shows no such method, you have your answer.
  • ▹Why it happens: the model fills gaps with names that sound right. Agents with good tool access usually catch this themselves when the import fails, so watch most closely in code paths your tests don't run.

5. Constraint drift

  • ▹What it is: the agent forgets an instruction it was following earlier, usually after context compaction or a long chain of tool calls.
  • ▹Tell: it starts doing something you explicitly ruled out, such as 'don't touch the public API', 'no new dependencies', 'Postgres only, no Redis', or 'keep the migration reversible.'
  • ▹10-second check: keep your two or three hard constraints in your head and check the newest diff hunks against them. If your harness shows a compaction or summary event, check right after it.
  • ▹Why it happens: summaries lose detail, and a constraint is a short sentence that looks unimportant, so it's easy to drop. Hard constraints belong in CLAUDE.md / AGENTS.md or the spec file, not only in your first chat message.

The 30-second glance

Don't watch the whole run. Check it at set points, with the same short checklist each time. For a typical 20 to 40 minute task, checking at about minutes 2, 5 and 10 works well. On longer runs, add a check every 10 minutes after that, and another right after any compaction event.

  • ▹Minute 2, plan check: read the stated plan. Does it match the spec? Does it name the right files? Is there anything the spec didn't ask for? Most scope creep, and many wrong approaches, show up here before any code is written.
  • ▹Minute 5, diff check: run the diff stat. Are these the files you expected? Has anything changed under tests/, config or CI? If so, open that hunk.
  • ▹Minute 10, loop and drift check: scan recent tool calls for repetition, compare the newest hunks with your hard constraints, and grep any name you don't recognise.
bash
#!/usr/bin/env bash
# glance.sh: run from the agent's worktree. Takes seconds to run and about 30 to read.
echo '== files touched =='
git diff --stat HEAD
git status --short   # also lists new untracked files, which git diff misses

echo '== test / config / CI changes =='
git diff --stat HEAD -- 'tests/' '**/*test*' '.github/' '*.toml' '*.cfg' '*.ini' \
  '.eslintrc*' 'tsconfig*.json' 'jest.config*' 'pytest.ini'

echo '== weakened assertions / new skips =='
git diff HEAD -U0 | grep -E '^\+.*(skip|xfail|\.only|is not None|toBeDefined|toBeTruthy|# type: ignore|eslint-disable)' || echo 'none'

echo '== new dependencies =='
git diff HEAD -- package.json pyproject.toml requirements*.txt go.mod | grep -E '^\+' || echo 'none'

The script doesn't decide anything for you. It collects the evidence in one place so a check is short enough to do between meetings. For loops, the transcript view in Claude Code or the tool-call log in Cursor, Codex or Aider is enough. You're looking for the same command three times in a row.

Kill, steer, or let it run

When you spot a tell, one question makes the decision quick: is the whole direction wrong, or just one assumption?

  • ▹Steer when the direction is right and one assumption is wrong. Send a short correction and let it continue. For example, the agent is fixing the timezone bug in the right function but assumes timestamps are stored in local time. You type: 'Timestamps in invoices are stored in UTC; convert at render time only.' The rest of its work still holds.
  • ▹Kill and restart when the plan is wrong. Don't try to steer a bad plan back on course, because the context is already full of the wrong framing. Stop the run, write down the tell, add one sentence to the prompt or spec that rules it out, and start clean. For example, at minute 3 the plan says 'migrate the date utilities to a new library first' when the task was a one-function bug fix. Kill it and restart with: 'Change only src/billing/dates.py. Do not modify tests. Do not add dependencies.'
  • ▹Let it run when there are no tells, even if it's slow. Being slow isn't a failure signature, and an agent that reads ten files before editing one is often doing the right thing. For example, at minute 12 it's still exploring, but the plan matches the spec, the diff only touches expected files, and every tool call is new. Leave it alone.

Two rules of thumb from my own teams. First, goalpost moving almost always means kill, not steer. Once an agent has learned that editing tests is allowed, it tends to do it again. Second, if you've already steered a run twice and it's still drifting, kill it. Needing a third correction means the prompt is wrong, not the agent.

Make it measurable

If you don't measure this, it stays a vague 'senior instinct.' You only need three things:

  • ▹Time-to-interrupt: minutes from the start of the run to your kill or steer. It should go down over time. Minute 4 is a good catch. Minute 30 means you're not actually doing the checks.
  • ▹Kill rate: the percentage of runs you kill. Very low can mean you aren't looking. Very high means your prompts and specs are missing detail. Watch your own team's trend instead of chasing a universal target.
  • ▹Tell note: one line per kill or steer describing what you saw. This is the most useful of the three.
text
# interrupt-log.md — team, week of 2026-10-05

| run | who | min | action | signature   | tell                                              |
|-----|-----|-----|--------|-------------|---------------------------------------------------|
| 101 | An  | 3   | kill   | scope       | plan proposed migrating to new date lib           |
| 104 | Minh| 6   | kill   | goalpost    | assertion loosened to 'is not None'               |
| 107 | Lan | 11  | steer  | invention   | used settings.BILLING_TZ, doesn't exist           |
| 109 | An  | 9   | kill   | loop        | same ImportError 4x, toggling relative imports    |
| 112 | Minh| 4   | kill   | goalpost    | added @pytest.mark.skip to flaky tz test          |
| 115 | Lan | 22  | kill   | drift       | added redis after compaction; spec said PG only   |
| 118 | Huy | 5   | steer  | scope       | touching api/serializers.py, out of scope         |

Read a week of these and the patterns jump out. In the log above, goalpost moving shows up twice, both times on date tests. That's a prompt problem, so add a standing rule to AGENTS.md: 'Never modify files under tests/ unless the task explicitly says so. If a test seems wrong, stop and ask.' The constraint-drift kill happened at minute 22, after compaction, so move 'Postgres only' out of the chat prompt and into the repo's agent instructions file, where it survives summarisation. Each recurring tell turns into a spec or prompt change, and next week's log should show that signature less often. The loop is: interrupts → tells → standing instructions → fewer interrupts.

You can also automate the cheapest checks. A hook that flags any write under tests/ during a non-test task (Claude Code hooks support this kind of PreToolUse check) turns a signature you'd otherwise have to catch by eye into an alert. Only automate signatures that already show up often in your log, and keep human checks for the ones that need judgment.

Teach it on purpose

  • ▹Transcript replay: keep transcripts of real runs, good and bad. In a 30-minute session with a junior, step through one and stop at each tool call to ask: 'Would you stop here? Why?' Reveal what actually happened only at the end. Juniors who already write good prompts learn quickly what a bad run looks like from the inside.
  • ▹Stop-the-run interview exercise: give the candidate a spec and a recorded agent transcript, and ask where they'd interrupt and what they'd do. Strong candidates point to a specific tell and suggest a specific correction. Weak ones read the final summary and say it looks fine. It tests judgment for agent work better than a whiteboard algorithm.
  • ▹Pair-glancing: for one week, a senior and a junior check the same runs separately and then compare notes. Most of the learning comes from the cases where they disagree.
  • ▹Scheduled repetition: practice is cheap here and easy to schedule. Every agent run is a chance to practise, and the interrupt log gives you feedback. Architecture taste takes years of projects to build. Interrupt literacy gets noticeably better after a few weeks of deliberate checks.

This week's exercise

  • ▹Choose your next five agent runs on real tasks.
  • ▹Check each one at about minutes 2, 5 and 10, using the checklist or the glance script.
  • ▹Write down every tell you see, including ones you decide to ignore, and label each with one of the five signatures.
  • ▹For each run, record two numbers: the minute you should have stopped it (in hindsight) and the minute you actually did, or 'never' if it ran to the end.
  • ▹At the end of the week, look at the gap between those two numbers. That gap is roughly how much you stand to gain. Turn your most common tell into one standing instruction in your repo's agent file.

On Day 26 tomorrow, we move from one engineer watching one run to a team supervising many agents at once. With five runs going in parallel you can't check every one, so the question becomes how to design runs that report their own tells.

Flashcards
Check yourself

Extend your knowledge

  • ▹Read Anthropic's 'Building effective agents' post and the Claude Code docs on hooks. Work out which of the five signatures (writes to tests/, for example) you could turn into a PreToolUse alert.
  • ▹Look up 'specification gaming' and 'reward hacking' in the AI safety literature. DeepMind's list of specification gaming examples is a good place to start. Goalpost moving is the day-to-day coding-agent version of the same problem.
  • ▹Read Gary Klein's 'Sources of Power' on recognition-primed decision-making. It explains how experienced people recognise situations from cues, which is why giving tells specific names helps turn intuition into something you can teach.
  • ▹Start an interrupt-log.md in your team repo this week, and review it at your next retro alongside your eval results.
Test yourself on this lesson →

Discussion

Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.

Ask me anything about “Interrupt literacy” — trade-offs, decisions, or the story behind it.