Skip to content

fix(edit-sync): retain buffer on force-post so a stale settlement-ack post survives#226

Merged
mtskf merged 3 commits into
mainfrom
fix/edit-sync-flush-trailing-keystroke
Jul 16, 2026
Merged

fix(edit-sync): retain buffer on force-post so a stale settlement-ack post survives#226
mtskf merged 3 commits into
mainfrom
fix/edit-sync-flush-trailing-keystroke

Conversation

@mtskf

@mtskf mtskf commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

Fix a data-loss race where the trailing keystroke typed during a tab switch (fast typing) is visibly erased. flush() force-posts the pending bytes with the current docVersion, which is stale the instant the host has settled the in-flight Edit (write lock released, ack still in transit). Such a force-post misses the host's lock-held stash path and is stale-rejected, so the authoritative Document reposts over the typed bytes — and nulling the buffer on force-post left nothing to replay.

Changes

  • src/webview/cm/edit-sync.tsflush() now RETAINS the buffer on a successful force-post instead of nulling it. The next ack replays it at the fresh docVersion via replayIfNeeded (which nulls the buffer on its own post → exactly one replay, never a loop). Double delivery is idempotent at the host via the no-op verdict on content equality.
  • Corrected the flush() / flushIfIdle() doc + impl comments that overstated the host-stash guarantee and only acknowledged the settlement→ack window for the handoff path.
  • test/webview/cm-edit-sync.test.ts — new red-first test pinning flush-during-the-settlement→ack window (bytes must survive via replay); updated the existing force-post test whose assertion pinned the buggy null-the-buffer contract.

Related

  • TODO: edit-sync flush() loses the trailing keystroke in the host-settlement→ack-in-transit window (Fable full review 2026-07-17)

Test Plan

  • Red-first test reproduces the loss, goes green with the fix
  • pnpm compile clean
  • pnpm test:unit green (3575 passed)
  • Biome clean on changed files

mtskf added 3 commits July 17, 2026 06:59
… post survives

flush() force-posts pending bytes with the current docVersion, which is
stale the instant the host has settled the in-flight Edit (write lock
released, ack still in transit). Such a force-post misses the host's
lock-held stash path and is stale-rejected, so the authoritative Document
reposts over the typed bytes. Nulling the buffer on force-post left
nothing to replay, so on an alive hide (tab switch during fast typing)
the trailing keystroke was visibly erased.

Retain the buffer on a successful force-post instead of nulling it: the
next ack replays it at the fresh docVersion via replayIfNeeded (which
nulls the buffer on its own post, so it is exactly one replay, never a
loop). Double delivery is idempotent at the host via the no-op verdict
on content equality. Correct the flush()/flushIfIdle comments that
overstated the host-stash guarantee and only acknowledged this window
for the handoff path.
…ent; pin loop invariant

Review-cycle findings (Codex + error-handler CRITICAL, test-analyzer HIGH,
comment-analyzer CRITICAL/MEDIUM):

- Gate the force-post buffer retention on there having been an Edit in
  flight. Unconditional retention also fired on the accept path (no prior
  lock held → base matches → host accepts and durably applies): the
  retained already-applied bytes could later replay over a racing external
  edit and silently clobber it, since the webview has no client-side
  conflict guard for a post-settlement replay. Retention now mirrors
  trySend (retain only under single-flight), which is the sole path to the
  stale settlement→ack window the fix targets.
- Correct the stale flushIfIdle comment that still described flush as
  force-post-then-null.
- Pin the 'exactly one replay, never a loop' invariant with a second
  reducer commit assertion, and add a test that the not-in-flight
  force-post nulls the buffer. Both revert-checked non-vacuous.
- Consolidate the triplicated retain rationale into the flush JSDoc.
@mtskf
mtskf merged commit 08a2d9a into main Jul 16, 2026
1 check passed
@mtskf
mtskf deleted the fix/edit-sync-flush-trailing-keystroke branch July 16, 2026 21:32
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