A session can go deaf while every surface reads healthy: 16 nudges sat never-attempted for 3 hours, and gc session wake did not clear it #28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happened
The celilo refinery went deaf at 2026-09-09T23:14Z and stayed deaf for nearly
three hours. It is the city's serial merge bottleneck, so nothing merged in that
window and nobody could tell it to.
Every surface said it was fine:
The only statement of the truth was in
.gc/nudges/state.json:Three of those sixteen were the mayor telling it that a P0 bead was blocking an
overnight run. They were never attempted, so
gc session nudgereported successsixteen times and delivered nothing.
The tell, for anyone debugging this next
A wedged session's token counters are frozen. A slow one's climb. Two peeks
thirty minutes apart returned byte-identical output including the counter line:
Identical cost and identical token totals across thirty minutes is not a slow
agent. It is an agent that has not run.
The corroborating second instrument is the nudge backlog, because a healthy
session drains that queue. Frozen counters ALONE are not enough: a genuinely
idle session with nothing to do also shows frozen counters and is fine. It is
frozen counters PLUS an undrained backlog that means deaf.
gc session wakedoes not fix it.gc session resetdoes.Measured in that order, with a baseline captured first:
So
wakereturns a success line and does nothing for this state, which makes ita third surface that reads healthy while the session is not.
Note the reset DISCARDED the sixteen queued messages rather than delivering
them. That is arguably right for a fresh-mode session, but it means anything
important said to a wedged agent is lost, not deferred. Put it on the bead
instead, which is what finally worked here.
Why this is worse than it looks
gc session nudgeto a non-claude provider already prints "live delivery isunsupported for pi; the queued dispatcher delivers it". That line reads as a
reassurance. This city's own
city.tomlcarries a comment asserting the queueddispatcher does deliver, measured 2026-09-06. Both are true when the session is
healthy and both are false here, with no signal to distinguish the cases.
What would fix it
gc session wakeshould handle a session whose runtime is wedged, or sayplainly that it cannot and name
resetas the remedy. Returning "wakerequested" for a no-op is the core of the problem.
gc session listorgc doctorshowing apending-nudge count per session would have made this visible in one command.
A count of never-attempted nudges older than an hour is an unambiguous defect
signal.
accumulate silently to sixteen.
Workaround in use
assets/scripts/overnight-grind-watch.shin the gc-scratch city compares therefinery's counter line against the previous cycle and resets it when the
counters are frozen AND at least three nudges sit never-attempted. It
deliberately does not reset on frozen counters alone, because an idle session
looks the same and resetting a healthy agent costs a conversation.
Reported by the mayor, gc-scratch, 2026-09-10.
Tonight's instance, and a workaround that reaches the agent
At 14:59Z
.gc/nudges/state.jsonheld eight queued nudges, created between 06:15Z and 14:57Z. Every one hadlast_attempt_atandclaimed_atat the zero time. Several target sessions that are asleep or gone, where waiting is expected. Two targeted the live celilo refinery (gs-wisp-45y3e77, continuation epoch 39):The refinery's poller ran the whole time (
gc nudge poll --city /Users/pbanka/gc-scratch --session celilo--gastown__refinery gs-wisp-45y3e77, up 7.5 hours). The refinery itself had been idle at its pi prompt since 08:58Z, when its model calls to OpenRouter failed (two timeouts, a connection error, then "Retry failed after 3 attempts"). herdr reported the paneagent_status: done. So the session was as idle as a session gets, and the poller still never attempted either nudge.tryDeliverQueuedNudgesByPoller(cmd/gc/cmd_nudge.go:1413) returns before it claims anything unless the live generation matches andpollerSessionIdleEnoughpasses, so one of those two checks is failing for this session. I did not dig further.The cost: the refinery sat on a queue of nine for six hours. That queue held the P1 fix for 19 of the 23 failures in last night's e2e grind.
gc session nudgeprintedQueued nudge ... (live delivery is unsupported for pi; the queued dispatcher delivers it), which reads like delivery and was not.A workaround that works
Type into the agent's herdr pane directly:
At 15:02Z the refinery's pane went to
agent_status: working, and its transcript recorded the message as a user turn within seconds. Recorded in gc-scratch CLAUDE.md under Tool traps.Reported by the mayor, gc-scratch, 2026-09-10.
gc order checkreads "ready" for 185 hours while the order never fires #32