Records the overrule and frames the slice (SS5b). No implementation;
the lane is PAUSED behind it.
**ALL THREE OF MY BOUNDS WERE WRONG**, and each is recorded because
each is why the trade looked cheap:
"self-inflicted" another frontend, or a background process, can
edit the same buffer --- the mapping moves with
`view_top` untouched and the clicking frontend
did nothing
"bounded by 3 rows" unbounded: ticks, paging, folds, edits and
reloads accumulate before the new frame is seen
"one round trip" until the frontend PRESENTS the replacement
frame; a slow or backed-up frontend widens the
window arbitrarily
With the narrowness gone there is nothing left to trade on.
**AND THE FIX IS A GENERATION, NOT A TOKEN.** A per-frame token would
invalidate a gesture on every repaint --- the exact mistake
`panel_epoch` exists to avoid, and it would break drags outright.
`mapping_generation` identifies the INVERSE MAPPING: it moves on
viewport, folds, wrap/gutter geometry and buffer content, and holds
across focus, styling, cursor motion and selection-only changes. The
stability half is load-bearing rather than an optimisation --- a drag
provokes selection repaints on every motion, and a generation that
moved with them would cancel the drag after one step.
SS5b frames the slice: appended variants (`PresentMapped`,
`PanelPointerMapped`) rather than widened structs, because postcard
encodes positionally and a shipped variant's field list is frozen;
bilateral gating, with neither side assuming the other's support and a
bare `PanelPointer` from a new-enough peer NOT silently upgraded; and a
drop-before-mutation check at the same rung of the ladder as the epoch
gates, one level finer. Dropping mid-drag is correct: if the mapping
changed, the cells being dragged across no longer mean what they meant.
This is where the hole always was --- `buffer_id` catches replacement,
`panel_epoch` catches close/reopen, `geometry_epoch` catches a
declaration race, and nothing catches "the text under that cell
changed".
Consequence: SS5b takes the next protocol version and GUI arc 1e's
`OpenTarget` moves to the one after. That edit is OWED BY THE 1b
BRANCH, which already revises `docs/gui-stage1-input-framing.md`
heavily; making it here would collide at the rebase 1b is scheduled
for, so it is recorded as required rather than made.
Gates: all nine green under `env -u TMPDIR`, log 20260814T155745Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai