AntiStallClaude is a harness-enforced Stop hook that blocks the
“announce-then-halt” drift — where an agent ends a turn with unfinished, authorized work.
Advice in a config file gets rationalized away. A hook can’t be.
On 0.1.0? Update to 0.1.1 — 0.1.0 had a
Stop-hook bug that could loop and burn tokens. Replace antistall-gate.py or re-run
install.py.
Autonomous agents stall mid-task. With work still queued and nothing actually blocking them, they write a tidy “here’s what I did, next I’ll…” summary — and stop. The tell is consistent: the moment the agent composes that summary, it has already decided to quit.
Advice. It lives in the model’s context and loses to in-the-moment reasoning (“clean checkpoint”, “the turn is long”).
Same advice, different file. Degrades over a long session.
Still text the model weighs — same failure class.
A separate process the harness runs every turn-end. The model can’t reason its way past code that isn’t its to run.
While a sprint is armed, ending a turn is blocked unless the agent writes a fresh, single-use stop-ticket declaring a legitimate reason:
DONE — whole queue finished BLOCKED — human-only decision QUESTION — needs an answer
The gate can never loop or trap a session. It honors
stop_hook_active — always allowing a stop that a prior block caused, so it nudges a drift-stop
at most once per continuation chain. A per-session, fail-open anti-loop cap (default 6) is a
second, independent guard: the counter is keyed on the session id so two agents never share it, and any
missing/empty/corrupt/unreadable/unwritable counter allows the stop rather than blocking again. When no sprint is armed, the
gate is silent — normal chat is never gated.
git clone https://github.com/scottconverse/AntiStallClaude.git python3 AntiStallClaude/install.py /path/to/your/project # defaults to the current dir # then RESTART the Code tab — hooks load at session start
Cowork note: user-level ~/.claude hooks don’t fire in Cowork — the installer
puts everything in the project .claude/, which does.
python3 .claude/hooks/antistall.py arm "build the export pipeline" python3 .claude/hooks/antistall.py done "shipped + tests green" # disarms python3 .claude/hooks/antistall.py blocked "need prod DB creds" # stays armed python3 .claude/hooks/antistall.py question "which storage backend?" python3 .claude/hooks/antistall.py status
It is not unspoofable — an agent could write a false DONE ticket.
But that converts a silent drift into an explicit, logged, auditable claim, and default-deny removes the
lazy path. It enforces don’t stop early, not do good work — pair it with
tests and CI.