Closes the rest of this slice's G9-G15 obligations. One production
change, one design correction found by mutation, and the rest witnesses.
G9b is the production change. `panel_motion_is_new` compared the cell
alone, so the FIRST motion after the mapping moved was eaten --- the
pointer has not travelled, but the cell now denotes different text, and
that is exactly the motion the daemon needs to re-anchor the gesture.
Now keyed by `(generation, cell)`. Deliberately read at the motion site
rather than reset from the frame path: resetting on every accepted
repaint would re-arm within one generation and bring pixel-rate traffic
straight back.
G9a, G9c, G10, G10a, G10c are witnesses over behaviour that was already
correct: a generation change ships even when the visible cells are
byte-identical; an identical frame at a higher generation still moves
authority; invalid frames, zero generations and lower generations are
each refused with frame AND generation retained; `Absent` does not erase
the high-water mark.
G15 is the TUI structural control, and its fixture IS the control: the
session has no `SemanticRenderState` at all, so every local click, drag
and wheel effect below is reached without a producer in existence. That
is a stronger claim than asserting a value was not consulted. Both wheel
ticks must land, for the same reason the mapped family needs its
exemption.
The design correction: G10a's first version asserted the zero refusal
with a generation already HELD. Mutating the zero check away left it
green --- zero is also *lower* than the held value, so the nondecreasing
clause refused the frame and the row proved nothing about zero. Zero is
only isolable before any authority exists, which is also the case the
framing names: a sender that never initialised the field. Split into its
own row with that setup, and the row says why.
Mutations, each biting only its named row:
- dedupe compares the cell alone -> G9b
- the frame path re-arms the dedupe on every accepted frame -> G9b
- daemon dedupes across the generation change -> G9a
- return early on frame equality before applying the generation -> G9c
- apply the generation before validating -> G10
- accept generation zero -> G10a (after the split; before it, this
mutation SURVIVED)
- `Absent` erases the high-water mark -> G10c
- a lower generation is accepted -> G10c
- panel input requires a token the TUI cannot have -> G15
Deferred, per SS5b's split table and unchanged here: G11b (exhaustion
cancellation), G12a/G12b (both two-tick wheel EFFECTS), G6c/G7c.
Verified: `cargo fmt --check`; `cargo clippy --workspace --all-targets
-- -D warnings`; `cargo test --lib` (1959); `cargo test -p pmacs-gpu
--bins` (280); `bottom_panel_stage1_acceptance` (47),
`bottom_panel_stage2b_daemon_acceptance` (39),
`bottom_panel_stage2b_gpu_acceptance` (2); `git diff --check`. Clippy
caught two findings in the new test code after the suites were already
green, which is why it runs as its own step.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai