Make Claude Code Finish the Job: Session Goals with Stop Hooks
intermediate 6 min read

Make Claude Code Finish the Job: Session Goals with Stop Hooks

Claude CodeHookssettings.json

Claude Code has one consistent failure mode in long sessions: it declares victory early. The plan was five steps, it finished three, the summary sounds confident, session over.

Stop hooks fix this. They run every time the session tries to end, and a non-zero exit means “not done, keep going.”

The mechanism

In .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "./scripts/goal-check.sh"
          }
        ]
      }
    ]
  }
}

If goal-check.sh exits non-zero, the stop is blocked. Whatever the script prints goes back to Claude as instructions. It reads the failure, and continues working.

Make the check real

The trap is writing a check that’s just words. Words let Claude argue. Scripts don’t.

My goal-check for this site runs the build and crawls every internal link:

#!/bin/bash
npm run check || { echo "Build failing, fix before stopping"; exit 1; }
broken=$(./scripts/linkcheck.sh | wc -l)
[ "$broken" -eq 0 ] || { echo "$broken broken links remain"; exit 1; }

While that exits 1, the session cannot end. Not “should not.” It cannot.

Where this changed my workflow

I stopped babysitting long sessions. Set the goal, set the check, walk away. The session either meets the condition or it’s still working when I come back. There’s no third state where it quietly gives up and writes a nice summary about the work it didn’t finish.

One rule I learned the hard way: scope the condition to things the session can actually verify. “Zero broken links” is checkable. “Site feels faster” is a hostage situation.

Frequently Asked Questions

What is a Stop hook in Claude Code?

A Stop hook is a command Claude Code runs whenever the session tries to end its turn. If the hook exits non-zero, the stop is blocked and Claude keeps working, with the hook's output injected as feedback.

Why not just write a detailed prompt instead?

Prompts describe intent; hooks enforce it. A long session drifts: context gets summarized, priorities blur. The Stop hook re-asserts the goal at the exact moment it matters, which is when Claude tries to quit.

Can the goal be something Claude can't verify, like 'rank first on Google'?

Set the condition on work you control, like pages built, schema present, or links passing, not on outcomes the session can't observe. Otherwise the hook can never release the session.

Mark Lester Mahilum

Mark Lester Mahilum

Claude Code expert based in the Philippines. Building AI-native workflows for businesses and documenting what actually works.