Repository navigation
fix: keep what Kelpie knows across a subscription rebuild - #8
Merged
Merged
Conversation
herdr only delivers agent status changes through per-pane subscriptions, and a subscription list is fixed for a connection's lifetime, so a changed pane set costs a whole new event connection. That rebuild went through the ordinary reconnect path, which clears `SessionState` and applies the next snapshot as `.bootstrap` — the phase that exists to keep a launch silent. Two things were lost with it. A pane that turned `blocked` inside the ~400 ms window never notified, which is what ROADMAP described. Less obviously, the pending reminders went too: a pane that was already blocked stopped being reminded about for good, because a bootstrap snapshot arms nothing. Starting or ending any agent session was enough to trigger it. `ConnectionRestart` names the distinction the loop was missing. A `.fresh` connection is a first sighting of herdr and bootstraps; a `.continuation` replaces a connection Kelpie closed itself, so it applies `.live` and carries `SessionState`, the reminders and the blocked emphasis forward. Timers are still torn down either way — the next snapshot reschedules all of them. Verified against a live herdr by running the fixed build and 0.1.11 side by side on the same events: with a pane blocked and a rebuild forced between the banner and the one-minute mark, the fixed build logged `reminding 1 blocked pane(s)` and 0.1.11 logged nothing. Also adds the notify breadcrumb that made that comparison possible; `fireDueReminders` already had its counterpart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verifying the rebuild fix against herdr 0.8.2 contradicted three things the repository asserted or assumed. `session.snapshot`'s `agents[]` does not carry plain shells: a pane split off with no agent in it never appears, so `isAgentPane` is currently filtering nothing. The guard stays — herdr's own schema declares `agent` optional, and that is the only promise Kelpie has — but the comments now say which is fact and which is defence. A snapshot request in flight fails on essentially every subscription rebuild, about ten milliseconds after the event connection closes, and succeeds on the first retry. `SnapshotRetry` was written believing the failure was a one-off from a replay burst. Two further facts are new rather than wrong, and go to CLAUDE.md: an agent appearing in a pane fires no event Kelpie subscribes to, so a newly started agent can be missing from the menu bar until an unrelated change or the 300 s resync; and a second `events.subscribe` on an open subscription connection makes herdr close it without an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last behavioural item in
ROADMAP.md, and it turned out to beworse than that entry described.
The bug
A changed pane set costs a whole new event connection — herdr delivers agent
status changes only through per-pane subscriptions, and a subscription list is
fixed for a connection's lifetime. That rebuild went through the ordinary
reconnect path, which clears
SessionStateand applies the next snapshot as.bootstrap, the phase whose entire job is keeping a launch silent.ROADMAP described the loss as "a pane that turned
blockedinside that ~1 swindow never notifies". The larger loss is the pending reminders: because a
bootstrap snapshot arms nothing, a pane that was already blocked stopped
being reminded about permanently. Starting or ending any agent session was
enough to trigger it, and the symptom is silence, so it is not the kind of bug
you notice.
The fix
ConnectionRestartnames the distinction the connection loop was missing..freshis a first sighting of herdr: bootstrap, start from nothing..continuationreplaces a connection Kelpie closed itself, so it applies.liveand carriesSessionState, the reminders and the blocked emphasisforward. Timers are torn down either way — the next snapshot reschedules all
of them.
If the reconnect after a rebuild fails,
rebuildRequestedis already cleared,so it falls into the normal path and everything resets as before.
Verification
Beyond
swift test(136 tests), the fix was measured against a live herdr byrunning the fixed build and the released 0.1.11 side by side on the same
events, with a probe agent driven through
pane.report_agent:blockednotifying 1 pane(s) entering blockedpane set changed; rebuildingpane set changed; rebuildingreminding 1 blocked pane(s)The notify breadcrumb added here is what made that comparison readable;
fireDueRemindersalready had its counterpart.The
AppCoordinatorwiring itself has no automated coverage — that file hasnone — so the rules live in
ConnectionRestartwhereswift testcan holdthem.
Doc corrections
The second commit fixes what live testing falsified.
agents[]does not carryplain shells, so
isAgentPaneis currently filtering nothing (the guard stays;herdr's schema declares
agentoptional). A snapshot request in flight failson essentially every rebuild rather than being the replay-burst one-off
SnapshotRetrywas written for. And two new protocol facts go toCLAUDE.md:an agent appearing in a pane fires no event Kelpie subscribes to, and a second
events.subscribeon an open connection makes herdr close it.🤖 Generated with Claude Code