The question before the prompt
Before you hand Claude Code three hours of work, decide whether the task can survive three hours without you. That depends far more on the task than on the model or the prompt. Two properties decide it:
- A verifiable end state. Can a command say "done" or "not done" without your opinion?
- A bounded blast radius. If the run goes wrong, what is the worst it can touch, and can you undo it?
Think of handing work to a contractor while you are on a flight. You would leave them "make the failing build green on this branch". You would not leave them "improve the billing system, and deploy when you're happy".
How long runs fail
Long runs fail in four recurring ways. Learn the names, because the rest of this course is tooling against them.
- Drift from intent. Each step is locally reasonable, yet the sum is not what you asked for. You wanted a bug fixed and got a refactored module.
- Context exhaustion. Everything Claude reads and runs fills the context window, and quality drops as it fills. Claude Code compacts the history automatically near the limit, and a summary can lose an early constraint such as "don't touch the public API".
- False claims of completion. Without a check to run, Claude stops when the work looks done. "All tests pass" in a final message is a claim until you see the output.
- Unintended side effects. A stray
rm, a migration applied to the wrong database, a push, an email sent. These are changes outside the diff you planned to review.
Gotcha: rewind is not a safety net for side effects. Claude Code's checkpoints restore files changed through its file-editing tools. Files changed by a Bash command (
rm,mv, a code generator) are not tracked, and nothing remote is: no database, deployment or sent message. Git is your real undo, and some things have no undo at all.
Three buckets
Sort every candidate task before you start it.
| Bucket | End state | Blast radius | Example |
|---|---|---|---|
| Delegable | A command checks it | Local and reversible with git | Migrate 80 files from one date library to another |
| Delegable with gates | A command checks it | Includes an irreversible or external step | Write and test a schema migration, then apply it to staging |
| Keep supervised | Needs your judgement | Hard to bound | "Make onboarding feel nicer"; anything acting on production |
A gate is a point where the run must stop and wait for a human. With a gate, the reversible 95% runs unattended and the irreversible 5% does not. In the migration example, Claude writes and tests the migration against a throwaway local database, and you apply it to staging yourself.
Walkthrough: triage in four steps
Take a real request: "Clean up the date handling."
Step 1: write the end state as a command. If you cannot, stop here, because the task is not ready.
# "Clean up" has no finish line. Pick one:
# every moment import in src/ replaced with date-fns, behaviour unchanged.
! grep -rq "from 'moment'" src/ # exits 0 only when no import remains
npx tsc --noEmit # the project still type-checks
npm test # the existing suite still passes
Step 2: list what the run can touch outside the working tree. Here that is nothing: no database, no network calls, no deploy. If it does go wrong, you can recover:
git switch -c chore/date-fns # the work lives on its own branch
# worst case afterwards:
git switch main && git branch -D chore/date-fns
Step 3: mark irreversible steps as gates. There are none in the edit itself. Pushing and opening a PR are external, so leave them out of the task.
Step 4: pick the bucket. The end state is checkable and everything is local and reversible, so the task is delegable.
The request now reads:
Replace every moment import under src/ with date-fns equivalents.
Do not change files outside src/. Do not edit or delete tests.
Done when all three pass:
! grep -rq "from 'moment'" src/
npx tsc --noEmit
npm test
Do not push. Show the output of each command when you finish.
Every failure mode now has something working against it. The grep gives a finish line, which counters drift. The checks are short and can be re-run after compaction, which helps with context exhaustion. The pasted output counters false completion claims. "Only src/, no push" limits side effects.
More rewrites
Vague: "Fix the flaky tests."
Checkable: "tests/checkout.spec.ts passes 20 runs in a row:
for i in $(seq 20); do npm test -- checkout.spec || exit 1; done"
Vague: "Speed up the build."
Checkable: "npm run build finishes in under 60s on this machine,
and npm test still passes."
Vague: "Make the dashboard look better."
Checkable: no honest command exists. Keep it supervised, or split out
the mechanical part (e.g. 'no axe accessibility violations').
Trade-off: a check can be gamed. An agent told only to "make
npm testpass" can get there by deleting the failing test. Pair the check with a constraint ("do not edit tests") and look atgit diff --statafterwards. A weak check is still better than none, but it only shows that the command passed. You still have to confirm the work is right.
Best practice: when in doubt, downgrade. If a task has one uncheckable part, split it. Delegate the mechanical half and keep the judgement half for yourself.
Key takeaways
- Judge the task by two things: an end state a command can check, and a blast radius you can bound and undo.
- The four long-run failure modes are drift from intent, context exhaustion, false claims of completion and unintended side effects.
- Sort work into delegable, delegable with gates, and keep supervised. A gate puts a human in front of the irreversible step.
- Rewind restores files edited through Claude's editing tools. It does not undo Bash changes or anything remote, so work on a git branch.
- Rewrite vague requests until "done" is an exit code, add constraints so the check cannot be gamed, and ask for the output as evidence.