A local pin/unpin fires reconcile synchronously via the
$pinnedSessionIds listener — before any PATCH exists — so
pullRemotePins() read the still-stale server row and immediately
undid the user's action: !row.pinned && heldLocally reverted a fresh
pin, and row.pinned && !heldLocally re-pinned a fresh unpin, after
which the push pass saw nothing to write and no PATCH ever fired.
Fence local intent ahead of the pull:
- Run the push pass first, so pending/unconfirmed record the local
intent before the page is read, then pull.
- Skip the pull for ids still in `pending` (row not yet resolved) —
local intent awaiting its PATCH is newer than any loaded page.
- Update mirrored bookkeeping before mutating the pin store inside
the pull, so the re-entrant reconcile doesn't echo adopted state
back as redundant PATCHes.
Regression tests cover both directions (fresh pin over a stale
pinned=false row, fresh unpin over a stale pinned=true row) plus the
deferred-pin case where the row loads stale after the toggle. All
three fail against the previous reconcile order.
Fixes#74570