[BUG] /goal evaluator fires during background-shell wait windows, fragmenting agent execution
Summary
The /goal evaluator currently treats the gap between a background-shell
spawn and its completion notification as a turn boundary, firing a goal
check during the wait window. The agent is not idle in this state — it
has explicit pending work (a long-running Bash invocation started withrun_in_background=true). The hook injects repeated "goal status"
reminders into the agent's context, fragmenting execution and producing
a stream of noise the agent may erroneously respond to instead of
continuing to wait.
This is a trigger-timing bug, distinct from existing /goal issues
that focus on what happens after the hook fires.
Distinction from related issues
| Issue | Focus | Relation to this report |
|---|---|---|
| #58550 | no circuit breaker — once stuck, burns tokens indefinitely | independent — this report is about whether the hook should fire at all in this state |
| #58516 | hook reaches the wrong conclusion (Goal Achieved while TaskList has pending items) | independent — noise is harmful regardless of conclusion |
| #58348 / #58465 | infinite loops from unsatisfiable conditions / ignored overrides | independent — but the same trigger-timing root cause amplifies all of these |
Fixing trigger-timing here would incidentally mitigate #58550 and
#58516 by reducing how often the evaluator runs in states where its
output is meaningless or wrong.
The agent has three distinct states — the hook conflates two of them
| State | When | Turn truly ended? | Hook should fire? |
|---|---|---|---|
| Tool-use in-flight | agent emitted tool_use, awaiting result | No | No (currently does not — correct) |
| Background-tool wait | agent spawned a long-running shell with run_in_background=true; spawn returned, but the work is incomplete and the agent is awaiting the completion notification | No — agent still has live, intentional pending work | No — but the hook does fire. This is the bug. |
| Truly idle | agent has finished its intent and is awaiting fresh user input | Yes | Yes |
The hook appears to treat the absence of a new tool_use as evidence
the agent is idle, collapsing state 2 into state 3.
Repro (conceptual)
/goal <any non-trivial goal>- Have the agent launch a long-running background shell, e.g.:
npm run devpytest tests/ --runslow(multi-minute run)- Any build / training / migration of several minutes
- Observe the goal evaluator firing during the wait window — once per
evaluator cycle — while the shell is still running and the agent is
doing nothing but waiting for the completion notification.
- Each fire injects a "goal status" reminder. The agent may:
- respond to the reminder instead of continuing to wait
- re-poll the background shell prematurely
- drift to unrelated actions
- in any case, fragment the planned execution flow
Expected
The evaluator fires only when the agent is truly idle — after the
agent's final response with no pending background work the harness is
tracking. Background-tool wait windows should be transparent to the
evaluator.
Why it matters
- Tokens — each fire reprocesses full context. #58550 quantified
~50% weekly budget burn from analogous over-firing.
- Agent behavior — repeated reminders during a wait can cause the
agent to abandon the wait, poll early, or branch into unrelated
work, defeating the point of run_in_background.
- UX — long-running commands now generate visible hook-fire noise.
- Cascading risk — more fires → higher probability of #58516
false-positive Goal Achieved.
Note
This report is mechanism-level and does not include a captured
transcript. Any sufficiently long run_in_background Bash invocation
under an active /goal should reproduce. Happy to add a captured
transcript if maintainers want one.
Environment
- Reporter platform: Windows 11
- Model: claude-opus-4-7
- Claude Code version: 2.1.140
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗