fix(desktop): keep fresh pin/unpin actions from being undone by pin sync - #76304
fix(desktop): keep fresh pin/unpin actions from being undone by pin sync#76304Yoki-cmd wants to merge 1 commit into
Conversation
baca5a5 to
1ee86be
Compare
|
Thanks for the focused regression coverage. The premise is confirmed on current main: A concurrent maintainer-authored alternative exists in #75973 ( Automated hermes-sweeper review. |
|
Independent reproduction and validation from a macOS ARM64 Hermes setup using Desktop as a thin client against a remote gateway:
I then checked out this PR at
The additional post-ack/sequential-pin case is important. A simpler push-before-pull reorder fixes the first click but can still lose a confirmed pin after One non-blocking documentation nit: the module header still says the whole local pin set is re-asserted at boot ( I did not install this branch on the user's live thin client; the evidence above is an independent code-path reproduction plus clean focused/full-suite validation. |
Three race windows in session-pin-sync could undo the user's pin state, each with a user-visible symptom: 1. Pinning a session silently failed: reconcile() runs the pull pass synchronously while the sidebar row still carries pinned=false (the PATCH has not been sent), so the fresh local pin was read as stale and removed before the write even left. 2. Unpinning silently failed: the stale pinned=true row made the pull pass re-adopt the pin the user had just removed. 3. Pinning a second session right after the first undid the first: the PATCH ack clears the unconfirmed guard, but the row keeps its old pinned value until the WS row update lands, so the next reconcile read the stale row as the server disagreeing and removed the pin. Fix: track ids added/removed since the last reconcile (lastSeen diff) and hold them out of the pull pass until their PATCH is written (pending / freshlyUnpinned guards); refresh the row's pinned field on the PATCH ack so the pull pass never reads our own confirmed write as stale. Pins present at boot stay subject to server authority in both directions. Adds tests for each fresh-write window; the existing remote-pull suite is unchanged and still green.
1ee86be to
a82a223
Compare
|
Thanks for the independent reproduction and the thorough validation — great to have the macOS/remote-gateway path confirmed, and glad the post-ack window test held up. The doc nit is addressed: the module header now states that the boot re-assertion applies except where a session row already carries an explicit |
|
Superseded by #80711. Same pin-sync race (fresh pin/unpin undone by the next reconcile). |
Symptom
On the desktop app, three user-visible failures in the pinned-sessions feature, all in
apps/desktop/src/store/session-pin-sync.ts:Root cause
reconcile()runs the pull pass (pullRemotePins) before the push pass, andunconfirmedonly guards writes that are already in flight. Each fresh local write (pin or unpin) has a window where the sidebar row still carries the pre-write server value and the PATCH has not been sent or acked:pinSession→set→ reconcile runs synchronously; the row still sayspinned=falseand the PATCH hasn't been sent, so the pull pass reads the new local pin as stale and removes it.pinned=true, so the pull pass re-adopts the pin the user just removed.writePinclears theunconfirmedguard on the PATCH ack, but the row keeps its oldpinnedvalue until the WS row update lands. The next reconcile (e.g. the user pinning a second session) then reads the stale row as the server disagreeing and removes the confirmed pin.Fix
lastSeen). Ids added since then are fresh pins — register them inpendingbefore the pull pass and skip the pull's undo branch for them. Ids removed since then are fresh unpins — skip the pull's adopt branch for them (freshlyUnpinned).pinnedfield in$sessionsso the pull pass never reads our own confirmed write as stale.The pre-existing pull pass behavior — adopting remote pins, dropping stale local pins the server reports unpinned, deferring pins until their row loads — is unchanged and still covered by the existing suite.
Tests
apps/desktop/src/store/session-pin-sync.test.ts: added 3 tests reproducing each fresh-write window (stalepinned=falsepage before the pin PATCH is sent; stalepinned=truepage before the unpin PATCH is sent; sequential pin-pin with the first row still stale). All 14 tests in the file pass; the existing 11 remote-pull tests are untouched.src/storesuite: 597 passed.