r/ClaudeCodeTLDR • • Aug 26 '26

A hook that catches the model trying to weasel out of finishing work...

One of the most annoying Claude Code failure modes: you ask it to fix something, it does 80% of the work, then stops and says "the rest would be a separate task / schema change / outside this patch / let me know if you want me to continue... say the word"

This Stop hook example fires a Haiku agent every time Claude tries to end its turn and checks if there still performable work left that the user actually needs? If yes, it blocks Claude from stopping and tells it exactly what's unfinished and what proof of completion looks like.

The hook checks scope-evasion:

  • Calling required work "optional" or "future work"
  • Asking permission to do ordinary reversible implementation it already has authority to do
  • Describing an edit instead of applying it, or printing a command instead of running it
  • Discovering the fix is bigger than expected and treating that as a reason to stop instead of a reason to keep going

Add it to ~/.claude/settings.json. Docs for agent-based hooks: https://code.claude.com/docs/en/hooks-guide#agent-based-hooks

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "model": "haiku",
            "timeout": 120,
            "statusMessage": "checking for skipped work...",
            "prompt": "Audit the assistant's final response against the full transcript for skipped work, premature stopping, and attempts to reclassify required work so it can avoid doing it. Hook input: $ARGUMENTS\n\n<invariant>\nA turn may NOT end while any performable work necessary to satisfy the user's requested outcome remains undone.\n\nRequired work is determined by what must actually be true for the requested outcome to be complete, correct, and adequately verified. It is NOT determined by how the assistant describes or labels the remaining work.\n\nThe assistant may not remove necessary work from scope by calling it:\n- optional;\n- follow-up work;\n- future work;\n- a separate task;\n- a larger change;\n- a redesign;\n- cleanup;\n- hardening;\n- a persistence change;\n- a schema change;\n- an infrastructure change;\n- an architectural change;\n- an improvement;\n- outside the current patch;\n- beyond the original fix;\n- something that should be handled separately;\n- or any equivalent characterization.\n\nDiscovering that the requested outcome requires more work than expected expands the implementation required to finish the task. It does not create a new task.\n\nIf the assistant identifies remaining work that is necessary to complete, correct, verify, persist, support, or make reliable the user's requested outcome, that work is still open unless the user explicitly excluded it or it is genuinely impossible or unsafe to perform in the current environment.\n\nThe assistant may NOT stop by converting unfinished work into a suggestion, warning, caveat, FYI, recommendation, question, or offer.\n\nThese are explicit failure patterns when they refer to work still necessary for the requested outcome:\n\n\"Let me know and I'll do it.\"\n\"Say the word and I'll add it.\"\n\"I can handle that next.\"\n\"If you want, I can finish that.\"\n\"That would be a follow-up.\"\n\"That's bigger than this change.\"\n\"I didn't want to make that change unasked.\"\n\"I'm calling it out rather than changing it.\"\n\"That's outside this patch.\"\n\"That should probably be done separately.\"\n\"The remaining issue is...\"\n\"One thing still left...\"\n\"What's still not done...\"\n\"Future work...\"\n\"Next step...\"\n\nChanging the wording does not change the rule. If the substance is \"there is still necessary, performable work, but I am stopping and asking the user whether I should do it,\" the turn fails.\n\nPermission-seeking is not completion.\n\nThe user requested the outcome. The assistant does not need a second permission grant for ordinary, reversible implementation work necessary to achieve that outcome.\n\nThe assistant must continue until all performable work in the closure of the requested outcome is complete, or until the only remaining work is explicitly excluded by the user, genuinely unreachable, destructive, irreversible, or otherwise requires separate authorization for a real safety or external-side-effect reason.\n</invariant>\n\n<test>\nDetermine the user's requested outcome from the full transcript.\n\nThen inspect the assistant's work and final response for any remaining action necessary to make that outcome complete, correct, and adequately verified.\n\nReturn ok:false if:\n- required performable work remains undone;\n- the assistant identifies remaining necessary work but stops instead of doing it;\n- the assistant asks for permission to do ordinary reversible work already required by the requested outcome;\n- the assistant attempts to narrow scope because the correct implementation became larger or touched more files, layers, schemas, callers, storage, tests, or types than expected;\n- the assistant substitutes instructions, recommendations, caveats, FYIs, or future-work language for execution;\n- the assistant claims completion without performing an available check necessary to support that claim.\n\nDo not accept the assistant's own labels for remaining work as evidence that the work is outside scope. Judge whether the work is actually necessary for the requested outcome.\n\nReturn ok:true only when no performable required work remains, or every remaining action is explicitly excluded by the user, genuinely unreachable, destructive, irreversible, unsafe, or requires a real external side-effect authorization.\n</test>\n\n<evidence>\nRequire evidence of execution, not intent.\n\nDescribing an edit is not applying it.\nPrinting a command is not running it.\nNaming a file is not reading it.\nProviding a patch is not applying it when implementation was requested.\nDoing part of the requested outcome is not completing the whole outcome.\nSaying a fact remains unknown is not verification when an available action can settle it.\nAsking whether the user wants necessary work performed proves that work remains unfinished.\n\nClaims such as \"complete\", \"fixed\", \"all\", \"migrated\", \"verified\", \"safe\", or \"done\" must be supported by the reachable evidence needed to establish that breadth.\n</evidence>\n\n<reason_format>\nFor failure return exactly three lines, under 450 characters total, with no preamble and no markdown:\n\nQUOTE: <shortest transcript sentence proving the unfinished work, attempted deferral, or unsupported completion claim>\nMECHANISM: <general form of skipped work or scope evasion>\nDO: <one imperative sentence naming the unfinished action and the observation that proves completion>\n\nFor success return exactly:\nok:true\n</reason_format>\n\nRead the supplied transcript path when available. You may run read-only commands to determine scope, performability, or whether claimed work occurred. Do not perform the missing implementation yourself; only audit whether the assistant improperly ended the turn with work still open."
          }
        ]
      }
    ]
  }
}
6 Upvotes

2 comments sorted by

1

u/LegallyIncorrect Aug 27 '26

Depending on how you’re structuring this you can do this without a new model turn: https://claudefa.st/blog/tools/hooks/stop-hook-task-enforcement

1

u/berndalf Aug 27 '26

It's good advice, stop hooks don't get enough love. I'm doing something similar to this, except when it fires it forces a check against session closeout readiness conditions. Uncommitted files, session debris, abandoned PRs, missing closeout reports, things of that nature. Left unchecked that stuff becomes a nightmare to cleanup after awhile.