Files
improvise/crates/improvise-ws-server
Edward Langley 253b84b2da fix(protocol): a refused frame is put back by the fold that made it (improvise-nxah)
`Client::key` folds a locally-resolved batch before sending it, so the page
redraws on the keystroke. A refusal left those folds in place: measured against
a real `Host`, a refused commit left the client one row down with its edit
buffer cleared while the host's mirror of that same client was still at the
origin holding the user's typing.

What rolls back is `ViewState` — the thing the fold produces — never a cache. A
`Command::Model` is a no-op in `reduce_view`, so the optimistic fold never
touched the caches, and the bug report's own evidence (`page_coords`) cannot
diverge this way: it moves only under `Projection::Grid`. What diverges is the
mode, cursor, status and buffers that ride along with the model command.

The rollback needed a positive acknowledgement first, which the protocol did not
have: with only `Rejected` on the wire, a client holding two frames in flight
cannot tell which one an answer addresses. `ServerFrame::Accepted` closes that —
emitted once per accepted mutating frame, last, after everything the frame
produced — the same shape as the basis-publication gap ADR 001 originally left.

Two existing facts make the rule usable rather than a convention: only
`Command::Model` can be refused, so a frame carrying none is answered by neither
and needs no entry; and `Rejection::refused_an_edit` already separates the two
`Save` refusals, so a failed write cannot consume the answer owed to an edit.
A refusal drops one frame and replays the rest, because a single saved snapshot
is unsound under pipelining — it would discard a later frame the host applied.

This does not do improvise-3al1's force, and it does not contradict it: the
2026-09-21 wave recorded the two tickets as contradictory on the ground that a
rollback would wipe the staged typing. It does the opposite — the typing frames
carry no model command, are never refused, and have already settled, so rolling
back the commit restores the buffer to the typing. Asserted, not inferred.

Reasoning: context/code/clients.md, context/code/protocol.md §4.
Evidence: context/test-evidence/clients.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-22 15:50:29 -07:00
..
2026-09-19 17:52:43 -07:00