Course · Codex · Daily workflow
Recover when a run goes sideways
Even with sharp briefs and the right posture, runs go sideways. The agent latches onto a fix that does not hold and keeps retrying it. It misreads the keyword table’s semantics and starts re-mapping categories in the wrong direction, confidently. It edits a file, the test fails, it edits back, the test fails again - a loop. None of this is a crisis. It is the ordinary failure mode of working with an agent, and the recovery is a small, repeatable procedure.
The operators who trust the tool are not the ones whose runs never go wrong. They are the ones who notice early, assess cheaply, and choose the right reset.
Interrupt the moment you see the loop
Section titled “Interrupt the moment you see the loop”The expensive mistake is watching. A loop costs tokens and time for every turn you let it spin, and the diff grows with each pass, so the cleanup you owe later is bigger the longer you wait. The moment you recognize the pattern - the same edit reappearing, the same failing test rerun with a cosmetic tweak - interrupt. Do not wait to see if the next iteration is the one that works; you have already seen it will not be.
Interrupt, then look before you speak. The instinct is to type “no, stop, do it differently” immediately, but that is a nudge on top of a confused context, and the last lesson explained why nudging a misread run is the slow road. First find out what actually happened.
Review the damage cheaply
Section titled “Review the damage cheaply”Before you read a single line of the diff, get the shape of it. git diff --stat tells you which files changed and by how much, in one glance. Inside a session, prefixing a line with ! runs a shell command under the current approval and sandbox settings:
> !git diff --stat
app/importers/csv.py | 6 +++--- app/services/categorise.py | 4 ++-- tests/test_import.py | 3 +- 3 files changed, 8 insertions(+), 5 deletions(-)(Shape shown for illustration; your run supplies the numbers.)
That one command answers the first question - how big is this, and where - without reading anything. Three files, modest churn, and the suite file barely moved, which already tells you the agent was editing implementation against an unchanged expectation. Only now do you read, and you read targeted: the specific hunk that is wrong, not the whole diff top to bottom. git diff app/services/categorise.py for the one file you suspect, or your editor’s diff view for the rest.
The discipline is the one you would apply to a colleague’s pull request - survey first, then zoom into the suspicious part - and it matters more here, because you are the one who decides whether any of it is kept.
This is also where the budget-audit profile pays off in reverse: if the run was exploratory and you had kept the sandbox read-only, there is no diff to review at all, and “recovery” is just asking a better question.
Steer the session, or clear it
Section titled “Steer the session, or clear it”With the damage assessed, you have two choices, and the decision is simpler than it feels.
Steer the current session when the context is still mostly right and only the last move went wrong. The agent knows the codebase, the brief, the constraints - you just need to point it off the one bad path. A short, specific correction works: “categories resolve by first keyword match, not best match - row order is the priority. Fix only app/services/categorise.py.” Steer when the loop was a local mistake in an otherwise sound run; the alternative discards good context to fix a small error.
Start a fresh session when the context itself is poisoned - the agent has talked itself into a wrong interpretation, the transcript has filled with noise about the wrong approach, or you simply cannot tell what it still believes. A fresh session with a sharper brief is then cheaper than steering: every correction typed into a confused context fights the wrong turns already in it. You met this in sessions & context - launch a new codex invocation carrying the brief you wish you had written first.
The tiebreaker is a single question: is the wrong part small relative to the whole context? Small - steer. The whole thing has drifted - clear. And when you do clear, fold what you learned into the new brief: the misreading that caused the loop becomes an explicit constraint this time, so the restart is not just fresh, it is sharper.
Where the stack’s arithmetic is loose
Section titled “Where the stack’s arithmetic is loose”The reference job throughout is the course’s constructed scenario: onboarding a fourth bank’s CSV export into budgetcli (the course’s running example, a small self-hosted personal-finance API in Python and FastAPI), first attempted cold on a “Monday” with a plain prompt. Stack the habits and the bookkeeping looks tidy: 11 exchanges on that cold Monday, minus 5 for the rules file and the profile together, minus 4 more for the skill, lands exactly on 2. None of these numbers was measured. The 11 is the cost the course defined for the cold Monday when it set up the scenario (chapter introduction), and the 6 and the 2 are the checkpoints the skills chapter defined for the job without and with the skill - reconstructions to count against, not logged runs. Treat the ladder as the scenario’s own bookkeeping, because that is all it is.
The skills chapter defined its checkpoint for the combination of AGENTS.md and the profile against a plain prompt, not for either one alone against a bare Monday. So the checkpoints hold - 11, then 6, then 2 - but nobody can say how many of those five turns were the rule’s and how many the profile’s. Techniques built and counted together do not come with itemized receipts.
The effort dial does not show up in that arithmetic at all. Route the fourth-bank job through low effort or high and the models chapter’s own finding still holds - the turn count does not move, because pattern-following work sits in the same corner as the category filter. What the dial buys is invisible on the exchange count and real on the token spend: matched effort keeps each turn cheap instead of quietly overpriced. A single ladder of turns cannot show every instrument’s contribution, because not every technique spends the same one.
And the ladder assumes the next export looks enough like the ones before it that the skill fires correctly. Say it does not - a fifth bank encodes negative amounts in parentheses instead of a minus sign, a shape the skill’s column-mapping never saw (a constructed example; the fourth bank was this course’s last). The skill mismaps the amount or stalls on a check it cannot satisfy, and you are back near the six-turn shape: interrupt, size the damage, steer or restart, spending turns the target never budgeted.
What does not regress is the hardening and the gate. The parse runs Decimal regardless of which bank’s format triggered the run, and any edit under app/importers/ or app/services/ re-runs the money tests through the hook before the turn ends. A bad run on an unanticipated format costs turns, not the money path.
The target is 2 turns and 0 money-path violations on the case the week actually built for. On a case it did not, the machinery half still holds, and the turn count is what you are negotiating with the recovery habit above.
That two-case result is the honest reading of the whole stack, stated once. On a format the skill knows, the machinery holds completely: 2 exchanges and no money-path violations, because the Decimal parse and the gate do not care which bank triggered the run. On a shape it has never seen, the machinery half still holds while the exchange count slips - the extra turns this lesson’s recovery habit exists for, still a fair trade against the scenario’s 11. Every technique in this stack came from a chapter that quantified it once, on the case it was built for, and none of them promised more than that. Close the course - look back at the whole arc on budgetcli and where to go from here.