Skip to content

fix: order session lists so states stop flapping and notifications repeat - #83

Merged
deymosh merged 5 commits into
masterfrom
fix/session-state-ordering
Oct 1, 2026
Merged

deymosh merged 5 commits into
masterfrom
fix/session-state-ordering

Conversation

@deymosh

@deymosh deymosh commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Problem

On the phone a session sometimes:

  • showed as stale when it wasn't;
  • flapped thinking → done → thinking → done with no work;
  • re-sent the same "finished" notification.

Cause (traced): the phone applied every session-list heartbeat as it arrived, with nothing to say which list was newest. The same list reaches the phone over every relay and over the direct link, the bridge can re-publish one, and after an app restart the direct link replays the last couple of minutes of the bridge's events. Any of these can apply an older list over a newer one, which:

  • steps a finished turn back to running;
  • marks a newer session stale;
  • re-fires "finished" when the newer list re-applies. The machine store is persisted, so this also happened across a restart.

Fix

  • protocol: sessions gets an optional rev, with a corpus fixture (valid and rejected).
  • bridge-core: every list carries a strictly increasing rev: the clock in ms, kept ahead of the stored last value, so a restart or a clock set back can't lower it. A list is also published only when a turn state actually changes.
  • client-core / client-runtime: each machine remembers the newest applied rev, persisted with the machine store. An older or equal list is ignored entirely: no state change, presence update or notification. A session-failed is announced only once.
  • docs: PROTOCOL.md describes the ordering.

Test plan

  • cargo clippy --workspace --all-targets -D warnings, cargo test --workspace
  • bridge e2e (-p bridge-runtime --test e2e -- --ignored)
  • New tests:
    • revs increase within a run and across a restart with the clock behind;
    • a late Running list and a replayed Idle list change nothing and announce nothing;
    • the rev survives the store;
    • a newer turn is announced again;
    • one failure is announced once.

🤖 Generated with Claude Code

deymosh and others added 5 commits October 1, 2026 03:11
The session-list heartbeat reaches a phone over every relay and over the
direct link, and a bridge may publish one again, so lists arrive out of
order, and a replay after a phone restarts brings old ones back. The
optional `rev` (the bridge's clock in ms, strictly increasing) lets a
phone apply only a list newer than the newest it applied.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Every session list carries a `rev`: the clock in ms, or one past the last
when the clock has not moved past it (two lists in one millisecond, or a
clock set back). The last one is stored, so a restarted bridge keeps
counting up.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The same session list reaches the phone over every relay and the direct
link, a bridge may publish one again, and after the phone restarts the
direct link replays the last minutes of the bridge's events. Each was
applied as it came, so a late list stepped sessions back (a finished turn
showed as running again, or a newer session as stale) and the step
forward afterwards announced the turn's end once more.

The phone now remembers, per machine and across restarts, the rev of the
newest list it applied and ignores any list no newer than that: no state
change, no presence update, no notification. A session-failed message is
also told once, not again when the same failure arrives a second time.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The agent repeats the turn state it is in (the Claude SDK reports
`requires_action` and `running` alike as running), and each report
published a new session list. Only a change of state now does.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@deymosh
deymosh force-pushed the fix/session-state-ordering branch from 892e65b to c35c6e1 Compare October 1, 2026 01:12
@deymosh
deymosh merged commit 19a1608 into master Oct 1, 2026
6 checks passed
@deymosh deymosh mentioned this pull request Oct 1, 2026
@deymosh
deymosh deleted the fix/session-state-ordering branch October 2, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant