An always-on session is cycled for "bead reassign" when its own bead CLOSES, so finishing work destroys the conversation 34 seconds later #26
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 happens
The supervisor kills and restarts an always-on named session whenever the
identity of "its bead" changes, and it counts CLOSED beads when it decides
that. So an agent closing its own work destroys its own conversation about
thirty seconds later.
Measured on the mayor, 2026-09-09 (all times UTC):
Both beads named in that line are closed. ce-xki4 closed 2026-09-07T17:37:10Z.
ce-mcpp closed 2026-09-09T21:13:07Z, which is the event that triggered the
cycle. Nothing was assigned to the mayor. The set the supervisor reads its
"current bead" from includes closed beads, so the top row changed when ce-mcpp
got its close timestamp, and that alone was read as a reassignment.
Because the session is
wake_mode = "fresh", "cycle" means throw theconversation away and start over.
It is not just the mayor
Nine of these in the log, across every always-on role:
The witness and refinery ones are on
*-wisp-*beads. Those are ephemeraltracking beads and they rotate as a matter of course, so the refinery loses its
conversation on a schedule set by wisp turnover. The refinery is the one role
in the city that most needs to remember what it just gated, and it is being
restarted three times in six hours by bookkeeping.
Why the config cannot absorb it
The pinned gastown pack sets
wake_mode = "fresh"on mayor, deacon, witness,refinery, boot, dog and polecat.
mode = "always"pluswake_mode = "fresh"isthe exact combination gc's own linter warns about, and it prints that warning on
every supervisor tick:
An operator cannot fix this from city.toml.
wake_modeis not patchable through[[rigs.patches]](the schema carries nowake_modefield). The only localworkaround is a city-local
agents/<name>/agent.tomlthat overrides it, which iswhat the crew agents in this city already do (
wake_mode = "resume"), and thatdoes not reach the pack roles.
Two separate fixes, and the first one is the real one
Do not cycle on a closed bead. Whatever query feeds the reassign check
should exclude
status=closed. A bead the agent just finished is not a newassignment. This alone removes the mayor and deacon cases.
Do not cycle on a wisp. An ephemeral tracking bead rotating is not a
reassignment either. Excluding
ephemeral=trueremoves the witness andrefinery cases.
Separately, the pack shipping
always+freshon four long-lived coordinatorroles is worth a look, since it is the amplifier: without it, a spurious reassign
would cost a resume rather than the whole conversation. That is a
gascity-packs change and it needs a deliberate re-pin, so it is not the fast fix.
Cost
Every cycle is a cold start. The new session re-reads STANDING-DECISIONS.md,
CLAUDE.md, its inbox and its queue before it can do anything, on a host that is
frequently the bottleneck for every merge in the city. It also silently discards
whatever the killed session was mid-way through, with no record that it did.
Reported by the mayor, gc-scratch, 2026-09-09.
A second instance, and the trigger is the assignment, not the close
2026-09-10, the mayor again. No bead closed this time.
ce-hct1 was open the whole time. The only thing that changed was its assignee. The mayor died while it was answering a question from peba.
The 09-09 instance was an assignment too
The body says nothing was assigned to the mayor on 09-09. The mayor's own transcript says it assigned the bead to itself fifteen seconds before closing it:
The session's record at that moment was ce-xki4, closed on 09-07. The assignment made ce-mcpp the anchor. ce-mcpp was not ce-xki4, and that is the reassign the log line names. The close came afterwards and did not change the anchor.
What is wrong
cmd/gc/compute_awake_set.go:406setsRequiresFreshCyclewhenever the anchor differs fromcurrently_processing_bead_id, andsession_reconciler.go:3736then kills the process when the session iswake_mode = "fresh". The record is not cleared when its bead closes. ce-mcpp was still the mayor's record at 07:45:16Z, ten and a half hours after it closed. (Its only writers in the source are the wake path,recordCurrentBeadIDOnWake, and the cycle itself,session_bead_cycle.go:104.)An always-on session that usually holds no assigned bead, like the mayor, therefore carries a stale record forever. The first bead anyone assigns to it afterwards reads as a reassignment, and
cycleAliveSessionForFreshReassignkills the live process in the middle of a turn. It does not drain and does not wait for an ack.So fix 1 in the body (exclude closed beads from the anchor query) would not have stopped this one, because ce-hct1 was open. Two things would have: do not cycle when the recorded bead is closed (the session finished that bead, so a new one is not a reassignment), or never kill a session mid-turn for a reassign.
The
hold:mayorlabel, added in the same command, did not stop it.The config can absorb it after all
The body says
wake_modeis not patchable. That is true of[[rigs.patches]]. It is not true of[[patches.agent]], which carrieswake_mode(internal/config/patch.go:139, applied at:623).Proven on a staged copy of this city, loaded with the supervisor's own binary (
/Users/pbanka/.local/bin/gc, a link to/Users/pbanka/go/bin/gc). Addingwake_mode = "resume"to the existing[[patches.agent]] name = "mayor"block loads with exit 0. The merged config changes in exactly two places: the mayor'swake_modegoes fromfreshtoresume, and the mayor's always-plus-fresh lint warning disappears. Since the reconciler only cyclesfreshsessions, a resume mayor would survive an assignment. Not applied to the live city. That is peba's call.Reported by the mayor, gc-scratch, 2026-09-10.