Close the producer, receiver, protocol-family, and gesture-lifecycle
cross-products in one pass. Separate route witnesses from replay effects,
freeze both old boundaries and new variant fields, and record the bounded
review method for the remaining chain.
Answers review of 15. Framing only. Three items reverse a rule 15
introduced, and one retracts a mutation that was not a defect.
**THE PRODUCER RULE CONTRADICTED PROACTIVE CANCELLATION.** The daemon
cancels BEFORE emitting the replacement frame, so the frontend needs
only to clear its local latch when that frame arrives, and send
nothing. Revision 15 asked it to emit a cancellation tail or retain the
latch: the tail is redundant --- the daemon would receive a release for
a gesture it has already settled, which is the duplicate release the
latch exists to prevent --- and RETAINING IS ACTIVELY HARMFUL, because
it manufactures a `Drag` under the NEW generation with no accepted
`Down`. That is the exact orphan the section exists to prevent,
produced by the rule meant to prevent it.
Ordering is what makes the simple rule safe: cancel, then emit. The
frame's arrival IS the cancellation signal; no second channel is
needed. Witnessed as `Down` -> key advances -> replacement frame ->
motion and physical `Up` produce no new drag and no duplicate release.
**THE LATCH HAD ONE TRIGGER AND NEEDED FIVE.** Cancellation runs on
every loss of gesture authority: generation advance, `Absent`, panel or
buffer identity change, geometry-epoch change EVEN AT AN UNCHANGED CELL
TOTAL, and detach. And an ordinary accepted `Up` must clear the latch,
or a later invalidation finds a gesture it believes live and
synthesises a duplicate release for a button already up --- the replay
lane's D1/D2 orphan race, arriving from the daemon's side.
**G9b's MUTATION WAS A VALID IMPLEMENTATION, NOT A DEFECT.** Keying the
dedupe by `(mapping_generation, coord)` preserves same-generation
suppression and naturally admits the first motion under a new
generation. Requiring it to fail would have forbidden a correct design.
Replaced with two real defects: compare only the cell and never key or
reset by generation (the first post-change motion is eaten), and reset
on every same-generation repaint (pixel-rate traffic returns).
**"PROJECTED CELL IDENTITY" CONTRADICTED THE STYLING CONTROL** in the
same section. The wire `Cell` derives `PartialEq` over `glyph`, STYLE
and `attachment` (`pmacs-protocol/src/cell.rs:153`), so an identity
keyed on cell equality moves on a pure recolour --- while the stable
controls rule style out. Terminal identity is now glyph and row
TOPOLOGY plus the view anchor, excluding face, style and cursor, with a
same-glyph/different-style control: the row that catches an
implementation reaching for `Cell` equality because it is right there.
P2s: zero-generation rows added in BOTH directions as independent legs
(a valid `PresentMapped` with generation zero must be rejected
atomically; a zero-generation `PanelPointerMapped` must be refused);
G7 split into outbound mapped-frame and inbound mapped-pointer legs,
since its old mutation only withheld the frame; G2's grid rows/columns
and fold-map-content/`fold_projection`-policy composites split; and
SS20 now names journey steps 5 and 8 while stating neither grade
changes --- an auditor scanning for grade movement alone would
otherwise conclude this slice touches no journey.
**AND R7 RECURRED, ON A DIFF THAT IS ENTIRELY DOCUMENTATION.** The
first `--protocol` run of this tree failed the `gpu` step on
`managed_retry_survives_transients_and_uses_the_successful_stream`,
with all three required fragments verified from the durable log
(`20260815T072601Z`). Recorded as R7's FIFTH occurrence.
It carries the strongest tree exclusion the row has had: occurrences 1
and 4 argued "unrelated lane", while this branch cannot be related at
all --- no Rust, no wire surface, no `pmacs-gpu` file. The line moved
to `attach.rs:1728` from `:1680`, which the row already treats as
occurrence-specific rather than a fragment. Isolated rerun green, and
the full gate green on the re-run (271/271 in the `gpu` step) --- which
per this file's rerun rule establishes INTERMITTENCE ONLY, though here
there is no tree change to exonerate.
What five occurrences across three flavors and five unrelated lanes now
support is that the failure is NOT LANE-CORRELATED. That is evidence
about where the cause is not. The retirement condition is unchanged.
Gates: all eleven green under `env -u TMPDIR` with `--protocol`,
log 20260815T073556Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of 14. Framing only. Four of the six reverse something
14 asserted, and a green protocol gate would not have caught any of
them.
**"BESIDE `Present`/`PanelPointer`" WAS POSITIONALLY DANGEROUS.**
"Beside" reads as adjacent, and adjacent insertion shifts every
discriminant below it --- the exact hazard the appended-only rule
exists for. Appended means LAST: `PresentMapped` after `Absent`,
`PanelPointerMapped` after `TextInput`, with a diagram so the next
reader cannot re-derive it wrongly. Field order is stated exactly,
`mapping_generation` is a `u64`, ZERO IS INVALID --- it is what a
default-constructed or half-initialised sender produces, so accepting
it would let a peer opt out of the check by sending nothing --- and the
gate reads `PANEL_MAPPING_MIN_VERSION = 25` rather than a literal.
**BLANKET REFUSAL STOPPED THE WHEEL AFTER ONE TICK.** The first
effective document wheel changes `view_top`, which advances the key, so
the next already-queued tick carries the old generation and is refused:
the panel scrolls once and goes dead until the frontend observes the
new frame. Local terminal scrollback has the same shape.
The discriminator is whether the gesture USES its coordinate.
Coordinate-free gestures --- the document wheel, non-reporting terminal
scrollback --- cannot be mis-aimed by a stale mapping and are EXEMPT.
A child-reported wheel is the opposite case: SGR carries row and
column, so a stale one aims an application action at a cell the user
never pointed at, and it keeps the check. Two-tick witnesses added,
because without them a blanket-refusal implementation passes every
single-event row in the matrix.
**CANCELLATION WAS REACTIVE AND LOSES A RACE.** If the replacement
mapped frame reaches the frontend before the physical `Up`, the
producer resets `pointer_held` and SUPPRESSES THE VERY EVENT that would
have cancelled --- so the daemon is never told, the selection stays
armed, and the child keeps holding its button. It is now PROACTIVE,
triggered by the authoritative key advancing while a gesture is
accepted, and the producer must emit a cancellation tail or retain the
latch rather than clearing first.
That needs state 14 assumed and never specified: an ACCEPTED-GESTURE
LATCH recording whether the `Down` was accepted, whether it reached the
child, and the coordinate, button and encoding a release must match.
Two rules fall out and are ruled here --- a stale `Up` with no accepted
`Down` is INERT, and cancellation NEVER reclaims a controller another
frontend has since taken, because a stale gesture must not steal a live
one's terminal.
**THE EXISTING SCREEN GENERATION CANNOT BE THE TERMINAL KEY.**
`Screen::changed()` bumps from 39 call sites including `SetStyle`,
`Bell`, the tab-stop operations, cursor-only motion and `SetTitle`.
None of those change what a coordinate denotes, so keying on it would
cancel a drag every time the child recoloured a character. A dedicated
terminal mapping revision is defined over projected cell identity,
retained-row identity and the per-view scroll anchor --- with those
five events as explicit STABLE CONTROLS, so a reader who later reaches
for the convenient counter fails a test instead of shipping a cancelled
drag.
**G5'S EFFECTS ARE NOT PROVABLE ON THIS BRANCH**, and 14 claimed them.
`gesture_last_content_cell` and the document/terminal replay exist only
on `panel-pointer-replay` (`pmacs-gpu/src/main.rs:2143` there); the
same struct here is at `:2124` with no such field. The obligations are
split in a table. G5a --- that the key advancing RAISES cancellation
--- stays here on purpose: the trigger is this slice's rule, and moving
the whole row out would leave the proactive ruling with no witness in
the slice that introduces it.
P2s: mutation legs split (wrap vs gutter, terminal content vs
scrollback, G5a-c, G8a/b, G9a/b); G7 given a positive-path mutation;
G11 expanded --- exhaustion must publish `Absent`, clear input
authority, cancel any accepted gesture and LATCH, or a stale panel
stays painted and permanently inert; the v26 correction finished at the
gate and old-peer cells (`:573`); and the SS20 impact statement added
--- hardens an existing panel island, no journey grade changes, no
config, no background work.
**And the pin correction is mine to make: it EXISTS**, at
`src/protocol.rs:1975`, in the ROOT crate's test module rather than
under `pmacs-protocol/` or `tests/` --- which is exactly where I
searched. `message.rs:524` was right and the doubt was wrong; the
contrary claim is removed from both records.
Gates: all eleven green under `env -u TMPDIR` with `--protocol`,
log 20260814T180105Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Own branch, own slice, protocol-bearing, runs alone. Framing only; no
implementation. Blocks `panel-pointer-replay`, which blocks GUI arc 1b.
Answers review of revision 13. Every item below reverses or completes
something 13 got wrong.
**GATING IS REFUSAL, NOT FALLBACK.** Revision 13 said a bare
`PanelPointer` from a new peer would be "handled under the old
semantics". That is a BYPASS: it leaves the exact hole this slice
exists to close, reachable by omitting a field. A >= v25 session
sending the legacy event is REFUSED before mutation, and a >= v25
frontend REJECTS a legacy `Present` rather than painting a band it
cannot safely hit-test. Only negotiated <= v24 keeps legacy semantics;
`Absent` stays common to both families.
**ONE AUTHORITATIVE PER-FRONTEND KEY**, used by projection AND inbound
validation, advanced after any mapping mutation and BEFORE the next
inbound pointer is handled --- whether or not anything has rendered.
Comparing against the last EMITTED frame recreates the hole, because a
mutation not yet painted has still changed the inverse mapping.
**STALE TAILS TERMINATE; THEY DO NOT VANISH.** A blanket drop breaks
liveness: a refused `Up` leaves an empty document selection armed with
a stale anchor, and leaves a reporting terminal child HOLDING A BUTTON
FOREVER. Cancellation is now a ruled outcome --- producer latch reset,
daemon selection and click-chain cleanup, and the child's release
delivered at the last coordinate known good. A cancelled gesture is
explicitly not a replayed one: the release is for liveness, and no
selection or scroll effect is applied from the stale event. Stale
BEGINNINGS may still simply drop.
**THE DOMAIN WAS INCOMPLETE.** `view_left` is added, because 1b makes
horizontal scrolling real. "Cursor movement is stable" is now
CONDITIONAL: a cursor move that triggers vertical or horizontal follow
changes `view_top` or `view_left` and therefore does change the
mapping. Terminal panels are ruled explicitly --- their coordinates are
decided by the SCREEN, so output and scrollback movement change the
generation while their buffer revision does not.
**SS5b HAD NO ACCEPTANCE MATRIX.** G1-G11 now cover the foreign edit
before render, every changing and stable domain entry ROW BY ROW, a
selection repaint that must preserve the generation and let a drag
continue, mid-gesture cancellation, v24 and v25 positive controls with
both wrong-family refusals, identical cells across a generation change
still emitting, atomic retention of frame and generation on an invalid
frame, and fail-closed exhaustion. The per-entry enumeration is
deliberate: one aggregate row cannot show WHICH input moved the key,
and a key ignoring `view_left` passes every vertical-only row.
**MAPPED MOTION KEEPS ITS COALESCING TAGS.** A new variant falling
through to the lossless default would put pixel-rate `Move`/`Drag` on a
bounded queue.
**PINS ACCUMULATE.** Revision 13 said the pin "moves", which would
delete coverage of the shape it protects. `PanelPointer` is retained;
exact `TextInput` bytes are added as the previous-final
`FrontendEvent`; the complete nested `PanelFrame(Absent)` bytes are
added as the previous-final `PanelFramePayload`. Recorded honestly: I
could find NO exact-bytes pin for `PanelPointer` anywhere in the tree,
though `pmacs-protocol/src/message.rs:524` says one is in the tests.
Either my search missed it or the doc overclaims; this slice resolves
it either way, since it must add exact pins regardless.
**AND THIS SLICE OWNS THE VERSION CORRECTION.**
`docs/gui-stage1-input-framing.md` now says 1e's `OpenTarget` is
**v26**, with the reason stated at the top. An expected rebase conflict
on `gui-stage1b-pointer-scroll` is not grounds for leaving the
canonical document false --- which is what I argued last round, and it
was wrong. `ADVERTISED_PROTOCOL_VERSION` stays pinned at 20.
Gates: all ELEVEN green under `env -u TMPDIR`, with `--protocol`
(`build-crdt`, `sweep-crdt`), log 20260814T162843Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Adds the approved framing for the bottom-panel arc: a buffer displayed
in a fixed-height window pinned to the bottom of the frame, targeted by
policy rather than by stealing the selected window, plus the missing
display-buffer/window-parameter concept underneath it.
Revision 4 follows three review rounds, an integration review, and a
landed-state audit against GPU initial target (#148, protocol v20) and
folding Stage 2 (#149).
Amended before branching by the pre-implementation dependency
verification recorded in section 0.6:
- the folding dependency is cleared and re-verified against canonical
main at ddaa80d (nothing in flight, folding Stage 2 acceptance 48/48
green, every borrowed anchor reproducing, and folding's only window.rs
edit confined to one 22-line hunk that leaves the layout functions
pre-folding code);
- R5-B1: Layout::compute has TWO production callers, not one. The
second, the peer-presence overlay pass in src/overlay_paint.rs, builds
its own text-area rect from active_layout() and never routes through
window_placements, so the planned compute(area, fixed) signature change
would otherwise leave every peer cursor painted at its no-panel row.
Corrected in section 1.1 and Q#BP2, pinned by acceptance 1, and the
fixed map is now specified to come from one shared helper rather than
being assembled per call site.
Stage 1 (window placement + TUI side windows) changes no wire shape.