To Stop Babysitting Claude, Give It a Failing Test
Autonomy is not a permissions problem, it is a verification problem. Claude can already run unattended — what is missing is a check it cannot talk its way past. Here are the three you can wire up today.
- #claude-code
- #testing
- #hooks
Everyone asking "how do I let Claude run without watching it" is asking the wrong question. You already can — auto mode approves the tool calls, and /loop and cron will re-run it all night. The reason you still hover over the terminal is not permissions. It is that you have no way to know whether the work is actually done, so you read every diff yourself. That is not supervision, that is being the test suite.
The fix is to stop being the check. Every hour of unattended agent work you want has to be bought with a check that runs without you, and there are exactly three places to put one.
The insight that reframes everything: /goal
/goal sets a completion condition and Claude keeps taking turns until it is met. After each turn, a small fast model — Haiku by default — reads the conversation and decides whether the condition holds. A "no" comes back with a reason, which becomes guidance for the next turn.
/goal all tests in test/auth pass and the lint step is clean
Now the constraint that tells you how to write every condition you will ever write: the evaluator does not run commands and does not read files. It can only judge what Claude has already surfaced in the conversation.
Sit with that. It means a condition like "the code is well structured" is worthless — nothing in the transcript proves it. But "pnpm test auth exits 0" works perfectly, because Claude has to run the command, and the output lands in the transcript where the evaluator can read it. The verifiable condition is not just better practice; it is the only kind that functions.
So a condition worth writing has three parts: one measurable end state, the command that proves it, and what must not change on the way there.
/goal every call site of the old session API is migrated.
Done means: `pnpm typecheck` exits 0, `pnpm test auth` exits 0,
no file outside src/auth/ is modified, or stop after 20 turns.
That last clause is not decoration. Conditions can run up to 4,000 characters, and a goal keeps going until it is met or you clear it — a turn limit is your circuit breaker.
The layer that does not negotiate: hooks
/goal is a model judging a transcript. Useful, but a model. When you need a check that holds regardless of what Claude decides, you need a hook — and the docs are explicit that hooks execute as shell commands at fixed lifecycle events, no matter what the model concluded.
A PostToolUse hook that runs after every edit and pushes failures back:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check.sh"
}
]
}
]
}
}
#!/bin/bash
input=$(cat)
file_path=$(jq -r '.tool_input.file_path' <<<"$input")
if ! npx eslint "$file_path"; then
jq -n '{decision: "block", reason: "Lint failed. Fix before continuing."}'
exit 0
fi
exit 0
The {"decision": "block", "reason": "..."} shape is the thing to learn. Your reason goes to Claude as feedback, so it self-corrects instead of stopping. You have turned your linter into part of the agent's loop rather than something you run afterwards and get annoyed about.
Two more worth knowing:
PreToolUseblocks before execution — exit2and yourstderrbecomes the blocking reason, or returnpermissionDecision: "deny"in JSON. This is where a rule you cannot express as a glob belongs.Stopfires when Claude thinks it is finished. Exit2and it is not finished. This is your "did you actually run the tests" gate, and it is the highest-leverage hook in the system.
/goal, in fact, is a session-scoped prompt-based Stop hook under the hood. Which tells you where the real power sits: a Stop hook in your settings file applies to every session in its scope, and can run a script for a deterministic check instead of asking a model.
The layer for work that outlives the session
/loop re-runs a prompt on an interval — /loop 5m check if the deployment finished. Omit the interval and Claude picks its own delay each iteration based on what it saw, between one minute and one hour.
Know the boundaries before you rely on it:
- Tasks only fire while Claude Code is running and idle. Close the terminal and they stop.
- Recurring tasks expire 7 days after creation — a deliberate bound on how long a forgotten loop can run.
- There is no catch-up. A missed fire fires once, not once per interval missed.
- A fresh conversation clears everything;
--resumerestores what has not expired.
For anything that must survive your laptop closing, that is the wrong tool — use Routines on Anthropic's infrastructure, a Desktop scheduled task, or a schedule trigger in GitHub Actions.
Pick by what should start the next turn
The clean mental model, which took me too long to arrive at:
/goal— next turn starts when the previous one finishes; stops when a model confirms your condition./loop— next turn starts when a time interval elapses; stops when you stop it or seven days pass.- Stop hook — next turn starts when the previous one finishes; stops when your script says so.
Polling a build? /loop. Grinding a migration to green? /goal. Enforcing "tests pass before you tell me you are done" across every session forever? Stop hook.
The uncomfortable conclusion
If your repo has no tests, you cannot have autonomous agents. Not "it works less well" — the mechanism is missing. /goal has nothing to evaluate, your hook has nothing to run, and you are back to reading diffs by hand.
Which reframes test coverage entirely. It stopped being about catching regressions for me and became the interface I use to delegate. The team I run writes tests faster now than before Claude Code, and not for a virtuous reason: an untested module is one nobody can hand to an agent, so it stays slow, and everyone notices.
Do this today
Take one task you would normally supervise. Write the /goal condition first, before any code — with a real command and an exit code in it. If you cannot write that line, you have just found the missing test, and writing it is the actual work. Then run the goal with auto mode on and go do something else.
Sources
Keep reading
More guides like this one.
The Claude Code Commands You Skip Are All the Same Command
There are over 60 built-in slash commands. Most people use four. Sort the ones you are missing by what they actually do and a pattern appears — almost all of them exist to manage one resource, and it is the one quietly wrecking your long sessions.
Your Rules Expire at Compaction. Here's Exactly Which Ones.
When a long session compacts, some of your instructions come back from disk and some silently do not. The split is documented and almost nobody knows it — which is why Claude "forgets your rules" three hours in.
Three Free Security Reviewers for AI-Written Code
If Claude writes a meaningful share of your code, you need a review layer that runs without you remembering to ask. Three exist, they are free, and one of them runs before the edit lands.