/goal hook fires "Goal Achieved" while TaskList tasks still pending/in_progress
Status Fixed / completed
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 3 comments · opened May 12, 2026 · closed May 25, 2026
Summary
The session-scoped /goal Stop hook fires a "Goal Achieved" notification mid-execution, even when the agent still has tracked tasks in pending / in_progress status via TaskCreate / TaskList. This produces user-visible confusion: the UI says the goal is achieved, but the agent reports the work isn't done.
Repro
- Start a long multi-step implementation in a single session.
/goal write plan, implement plan- Agent writes an 11-task plan via
writing-plansskill and starts executing throughsubagent-driven-development. - Tasks 1–8 land as commits. Tasks 9 (parity sweep), 10 (backtest validation), 11 (final verification + handoff) still tracked as
pending/in_progress. - UI surfaces "Goal Achieved" while the agent is still mid-execution.
- User asks "is this done?" — agent (correctly) answers "no, 3 tasks remain."
Expected
Either:
- The
/goalhook consultsTaskListand only fires when nopending/in_progressitems remain, OR - The notification text is clarified to indicate it's an advisory match against the natural-language condition, not a definitive completion check.
Actual
The hook appears to evaluate the natural-language goal condition against some loose heuristic (presence of plan + most impl commits) without consulting the structured task tracker. Result: false-positive "achieved" while tracked work remains.
Environment
- Claude Code CLI
- Model: claude-opus-4-7
- Long-running background session with 30+ subagent dispatches
- Goal text:
write plan, implement plan
Why it matters
Users rely on hook notifications as a signal of completion. A premature "achieved" undermines that signal and forces users to manually cross-check TaskList to know what's actually done.
3 Comments
The shape of this bug looks structurally identical to a class of divergence I've been tracking across the issue tracker: a confirmation surface fires against a natural-language heuristic while a structured information source in the same session contradicts it. Issue #57271 has the same shape with the rendering layer: the agent claims a report was "verified" against a reference, but the rendering layer was never actually consulted, only the parsed numeric tokens. The Goal Achieved notification in this issue lives at the same layer of mismatch.
The interesting design question your repro raises is which information source the hook should treat as authoritative when they disagree. The natural-language goal text says "write plan, implement plan", and that is what the heuristic is matching against. Eight commits have landed; the heuristic finds enough signal to fire. The TaskList tracker says three tasks are still in flight. Both reads are honest, given their evidence; the hook only sees one.
A correctness-oriented fix is Expected #1 from your post: the hook consults TaskList when it exists in the session, and treats any
pending/in_progressitems as a hard gate against firing. The edge case is sessions without a TaskList. The hook should not require its presence (many workflows don't use the structured tracker), but the rule is monotonic: if TaskList exists and has open items, the goal is by definition not yet achieved. There's no case where TaskList says "open" and the goal is correctly fireable.A communication-oriented fix is Expected #2: rename the notification to reflect what the heuristic actually does. "Goal condition matched" is the literal evaluator output. "Goal Achieved" is the operator-facing claim. Right now the two are conflated. Even if the heuristic is kept as-is, decoupling the notification text from the evaluator's confidence would surface the gap to operators who don't read the source code.
The robust answer is probably both: the hook gates on TaskList when present, and the notification text is downgraded to "Goal condition matched (no TaskList tracker, verify externally)" when TaskList is absent. That way the operator never sees a definitive completion claim without the structured evidence that supports it.
As an interim workaround for your specific session shape: a PostToolUse hook in
~/.claude/hooks/that intercepts the Goal Achieved event and queries the TaskList tool one more time before surfacing the notification can give you the gate behavior without waiting for the upstream fix. The shape is roughly "if the most recent TaskList output contains anypendingorin_progressitems, suppress the achievement banner and emitGoal condition matched but TaskList shows N items openinstead." It's a 20-30 line shell script that closes the false-positive surface for your workflow until the upstream behavior is settled.This is the same defense pattern the rule-enforcement layer uses generally: the prompt-layer or heuristic-layer claim is unreliable for safety-critical signals, so the hook layer cross-checks against the structured data and gates the visible message accordingly. In your case the safety-critical signal is "is the agent done?", and the hook can answer it more reliably than the natural-language evaluator.
Closing — no longer tracking this on my end. Feel free to reopen if it's still relevant.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.