docs(framing): fold the mapping-generation closure audit into revision 16
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.
This commit is contained in:
parent
137c7fc736
commit
775a046ce4
|
|
@ -281,8 +281,8 @@ from #171 and #215.
|
||||||
**`githubsucks/panel-mapping-generation` is the authoritative tip.**
|
**`githubsucks/panel-mapping-generation` is the authoritative tip.**
|
||||||
Recover with `git fetch githubsucks && git checkout
|
Recover with `git fetch githubsucks && git checkout
|
||||||
panel-mapping-generation`.
|
panel-mapping-generation`.
|
||||||
- **No PR yet. Checkpoint: framing revision 16 (§5b) AWAITING
|
- **No PR yet. Checkpoint: framing revision 16 (§5b) APPROVED
|
||||||
APPROVAL; NO IMPLEMENTATION.**
|
2026-08-15; NO IMPLEMENTATION.**
|
||||||
- **PROTOCOL-BEARING — v25, and it runs alone.**
|
- **PROTOCOL-BEARING — v25, and it runs alone.**
|
||||||
`ADVERTISED_PROTOCOL_VERSION` stays pinned at **20**.
|
`ADVERTISED_PROTOCOL_VERSION` stays pinned at **20**.
|
||||||
- **Why it exists.** A `PanelPointer` names a cell and nothing on the
|
- **Why it exists.** A `PanelPointer` names a cell and nothing on the
|
||||||
|
|
@ -294,6 +294,10 @@ from #171 and #215.
|
||||||
**overruled**, because a **foreign** edit moves the mapping with
|
**overruled**, because a **foreign** edit moves the mapping with
|
||||||
`view_top` untouched, the error is unbounded, and the window lasts
|
`view_top` untouched, the error is unbounded, and the window lasts
|
||||||
until the frontend **presents** the new frame.
|
until the frontend **presents** the new frame.
|
||||||
|
- **Semantic seam only.** The TUI hit-tests current daemon state
|
||||||
|
directly, receives no `PanelFrame`, and carries no mapping token. Its
|
||||||
|
existing panel click/drag/wheel effects are a structural control
|
||||||
|
against moving the generation gate into shared replay.
|
||||||
- **A generation, not a per-frame token.** A token moving with the
|
- **A generation, not a per-frame token.** A token moving with the
|
||||||
frame would cancel a drag on the next repaint — the mistake
|
frame would cancel a drag on the next repaint — the mistake
|
||||||
`panel_epoch` is stable to avoid. The key moves with the inverse
|
`panel_epoch` is stable to avoid. The key moves with the inverse
|
||||||
|
|
@ -308,35 +312,61 @@ from #171 and #215.
|
||||||
- **Gating is REFUSAL, not fallback.** A ≥ v25 session sending bare
|
- **Gating is REFUSAL, not fallback.** A ≥ v25 session sending bare
|
||||||
`PanelPointer` is refused; a ≥ v25 frontend rejects legacy
|
`PanelPointer` is refused; a ≥ v25 frontend rejects legacy
|
||||||
`Present`. Only ≤ v24 sessions keep legacy semantics; `Absent` is
|
`Present`. Only ≤ v24 sessions keep legacy semantics; `Absent` is
|
||||||
common.
|
common. The reciprocal wrong-family cases are witnessed too: a v24
|
||||||
|
daemon refuses `PanelPointerMapped`, and a v24 frontend rejects
|
||||||
|
`PresentMapped`. Compiling the variant in the same crate never
|
||||||
|
overrides the negotiated family.
|
||||||
- **Stale tails TERMINATE, they do not vanish.** A dropped `Up` leaves
|
- **Stale tails TERMINATE, they do not vanish.** A dropped `Up` leaves
|
||||||
an empty selection armed and a reporting child holding a button
|
an empty selection armed and a reporting child holding a button
|
||||||
forever, so cancellation is a ruled outcome: producer latch reset,
|
forever, so cancellation is a ruled outcome: producer latch reset,
|
||||||
daemon selection/click-chain cleanup, and the child's release
|
daemon selection cleanup, and the child's release delivered.
|
||||||
delivered.
|
Click-chain invalidation is a separate mapping-identity transition;
|
||||||
|
it also runs when no gesture is held.
|
||||||
- **Pins ACCUMULATE**: keep `PanelPointer`, add exact `TextInput`
|
- **Pins ACCUMULATE**: keep `PanelPointer`, add exact `TextInput`
|
||||||
bytes (previous-final `FrontendEvent`) and the complete nested
|
bytes (previous-final `FrontendEvent`) and the complete nested
|
||||||
`PanelFrame(Absent)` bytes (previous-final `PanelFramePayload`).
|
`PanelFrame(Absent)` bytes (previous-final `PanelFramePayload`), then
|
||||||
|
add exact encode/decode pins for both new mapped variants with
|
||||||
|
distinct same-typed field values. Previous-final pins protect append
|
||||||
|
position; new-variant pins protect their own frozen field order.
|
||||||
**The `PanelPointer` pin DOES exist** at `src/protocol.rs:1975` —
|
**The `PanelPointer` pin DOES exist** at `src/protocol.rs:1975` —
|
||||||
revision 14 recorded it as missing, which was wrong: it lives in the
|
revision 14 recorded it as missing, which was wrong: it lives in the
|
||||||
root crate's test module, not under `pmacs-protocol/` or `tests/`.
|
root crate's test module, not under `pmacs-protocol/` or `tests/`.
|
||||||
- **Appended means LAST**: `PresentMapped` after `Absent`,
|
- **Appended means LAST**: `PresentMapped` after `Absent`,
|
||||||
`PanelPointerMapped` after `TextInput`. `mapping_generation` is a
|
`PanelPointerMapped` after `TextInput`. `mapping_generation` is a
|
||||||
`u64`, **zero invalid**, gated by `PANEL_MAPPING_MIN_VERSION = 25`.
|
`u64`, **zero invalid**, gated by `PANEL_MAPPING_MIN_VERSION = 25`.
|
||||||
|
- **Frontend generation is a session high-water mark.** Higher values
|
||||||
|
may skip, equal-generation style/selection repaints are valid, lower
|
||||||
|
values are rejected atomically, and `Absent` does not erase the
|
||||||
|
high-water mark. Detach does.
|
||||||
- **Coordinate-free wheels are EXEMPT** from the freshness check —
|
- **Coordinate-free wheels are EXEMPT** from the freshness check —
|
||||||
otherwise the first tick advances the key and the next queued tick is
|
otherwise the first tick advances the key and the next queued tick is
|
||||||
refused, so the panel scrolls once and dies. Child-reported terminal
|
refused, so the panel scrolls once and dies. Child-reported terminal
|
||||||
wheels keep the check, because SGR carries row and column.
|
wheels keep the check, because SGR carries row and column.
|
||||||
- **Cancellation is PROACTIVE** — the daemon cancels, THEN emits the
|
- **Cancellation is PROACTIVE**, at the transition that revokes
|
||||||
replacement frame, and the frontend **clears its latch on receiving
|
authority: mapped-generation advance before its frame, before
|
||||||
that frame and sends nothing**. Revision 15 asked the producer to
|
daemon-authored `Absent` or panel/buffer replacement, when a new
|
||||||
emit a tail or retain the latch; retaining it manufactures a `Drag`
|
geometry declaration is accepted, and at detach. The last four apply
|
||||||
under the new generation with no accepted `Down`.
|
to both v24 legacy and v25 mapped gestures. It is taken exactly once;
|
||||||
- **The latch dies on EVERY loss of gesture authority**: generation
|
no producer cancellation tail is sent.
|
||||||
advance, `Absent`, panel/buffer identity change, geometry-epoch
|
- **Producer reset has cause-specific signals**, not one generic
|
||||||
change **even at an unchanged cell total**, and detach — **and on an
|
replacement frame: accept the valid generation/identity frame;
|
||||||
ordinary accepted `Up`**, or a later invalidation synthesises a
|
accept `Absent`; locally advance the geometry declaration; or tear
|
||||||
duplicate release.
|
down the frontend. Invalid frames and same-identity repaints retain
|
||||||
|
the latch.
|
||||||
|
- **The accepted-gesture latch is left-button-only and common to both
|
||||||
|
wire families.** It arms only after accepted `Down(Left)` and dies on
|
||||||
|
accepted `Up(Left)` or any authority loss. Move, wheel, context, and
|
||||||
|
right/middle presses do not arm it; otherwise the GPU's press-only
|
||||||
|
right path manufactures a later release. A terminal Down also fixes
|
||||||
|
child-reporting versus local-selection domain through Drag/Up, so a
|
||||||
|
later Shift or reporting-mode change cannot orphan or invent a child
|
||||||
|
release.
|
||||||
|
- **Click-chain state is independent and per frontend.** It survives
|
||||||
|
an ordinary `Up` by design, but every mapping identity change clears
|
||||||
|
or re-keys it even when no gesture is held. A completed click followed
|
||||||
|
by a foreign edit and same-cell click is single, not double; an
|
||||||
|
intervening click from another frontend does not erase the first
|
||||||
|
frontend's legitimate chain.
|
||||||
- **Terminal identity EXCLUDES style.** Wire `Cell` equality covers
|
- **Terminal identity EXCLUDES style.** Wire `Cell` equality covers
|
||||||
`glyph`, `style` and `attachment`
|
`glyph`, `style` and `attachment`
|
||||||
(`pmacs-protocol/src/cell.rs:153`), so keying on it would move the
|
(`pmacs-protocol/src/cell.rs:153`), so keying on it would move the
|
||||||
|
|
@ -345,10 +375,34 @@ from #171 and #215.
|
||||||
- **The terminal key is NOT `Screen`'s generation** — that advances
|
- **The terminal key is NOT `Screen`'s generation** — that advances
|
||||||
from 39 sites including style, bell, tab-stops and cursor-only
|
from 39 sites including style, bell, tab-stops and cursor-only
|
||||||
motion, none of which change what a coordinate denotes.
|
motion, none of which change what a coordinate denotes.
|
||||||
- **G5's EFFECTS are owed by the rebased replay lane**, not provable
|
- **G5 is now an explicit transition matrix.** It crosses every common
|
||||||
here: `gesture_last_content_cell` and replay exist only on
|
authority loss with v24 and v25, distinguishes each producer reset
|
||||||
`panel-pointer-replay`. G5a — that the key advancing raises
|
signal from invalid/same-identity controls, pins ordinary-Up
|
||||||
cancellation — stays in this slice.
|
exact-once and left-only arming, and tests click identity without a
|
||||||
|
live gesture. This slice owns only the new mapping-generation signal
|
||||||
|
and mapped-frame state; the rebased replay lane owns the common
|
||||||
|
invalidations and every document, terminal, complete producer-latch
|
||||||
|
and click-chain effect through real call sites. No dead
|
||||||
|
classification helper is added on a base without replay.
|
||||||
|
- **Routing and effects are separate controls.** This slice can prove
|
||||||
|
v24/v25 events reach the existing focus path, but selection,
|
||||||
|
terminal reporting, stable-generation drag continuation, two-tick
|
||||||
|
wheels and exhaustion cancellation remain explicit replay-lane rows;
|
||||||
|
no classification-only result is reported as end-to-end input.
|
||||||
|
- **Review-process correction — carry into the next handoff
|
||||||
|
absorption.** Revision 16 replaces finding-at-a-time review with one
|
||||||
|
closure pass for this chain and future framings: verify cited ground
|
||||||
|
truth; enumerate trigger × protocol family × target × lifecycle;
|
||||||
|
separate producer, route, receiver and effect; prove every witness's
|
||||||
|
discriminating setup and positive control; check that each proposed
|
||||||
|
mutation is actually defective; and assign every row to the branch
|
||||||
|
that can reach its real call site. Protocol work also checks all four
|
||||||
|
wrong-family quadrants, exact pins and version fallout. Findings are
|
||||||
|
returned as one batch. If three revisions do not close a framing,
|
||||||
|
stop serial review and perform/fold this complete audit rather than
|
||||||
|
opening another one-finding round. The overdue #239/#240 handoff
|
||||||
|
absorption must promote this rule; this feature branch does not edit
|
||||||
|
the deliberately lagging canonical handoff.
|
||||||
- **Owns the version correction.** `docs/gui-stage1-input-framing.md`
|
- **Owns the version correction.** `docs/gui-stage1-input-framing.md`
|
||||||
moves 1e's `OpenTarget` to **v26** here — an expected rebase
|
moves 1e's `OpenTarget` to **v26** here — an expected rebase
|
||||||
conflict on `gui-stage1b-pointer-scroll` is not grounds for leaving
|
conflict on `gui-stage1b-pointer-scroll` is not grounds for leaving
|
||||||
|
|
|
||||||
|
|
@ -1772,23 +1772,49 @@ cannot preserve the old panel-focused attach leak.
|
||||||
|
|
||||||
## 5b. The cell-mapping generation — a protocol slice (Q#BP-R3)
|
## 5b. The cell-mapping generation — a protocol slice (Q#BP-R3)
|
||||||
|
|
||||||
**Status: revision 16 — AWAITING APPROVAL. Nothing implemented.**
|
**Status: revision 16 — APPROVED 2026-08-15. Nothing implemented.**
|
||||||
Revision 16 answers review of 15; three of its items reverse a rule 15
|
Revision 16 answers review of 15; three of its items reverse a rule 15
|
||||||
introduced:
|
introduced:
|
||||||
|
|
||||||
- **The producer rule contradicted proactive cancellation.** Since the
|
- **The mapped-generation producer rule contradicted proactive
|
||||||
daemon cancels *before* emitting the replacement frame, the frontend
|
cancellation.** Since the daemon cancels *before* emitting that
|
||||||
simply **clears its latch on receiving that frame and sends
|
successor frame, the frontend simply **clears its latch on accepting
|
||||||
nothing**. 15's "emit a tail or retain the latch" was redundant in
|
the frame and sends nothing**. 15's "emit a tail or retain the
|
||||||
the first case and **actively harmful** in the second — retaining it
|
latch" was redundant in the first case and **actively harmful** in
|
||||||
manufactures a `Drag` under the new generation with no accepted
|
the second — retaining it manufactures a `Drag` under the new
|
||||||
`Down`, the exact orphan the section exists to prevent.
|
generation with no accepted `Down`, the exact orphan the section
|
||||||
|
exists to prevent. Other authority losses have their own reset
|
||||||
|
signals below.
|
||||||
- **The latch had one trigger and needed five.** Cancellation runs on
|
- **The latch had one trigger and needed five.** Cancellation runs on
|
||||||
every loss of gesture authority: generation advance, `Absent`, panel
|
every loss of gesture authority: generation advance, `Absent`, panel
|
||||||
or buffer identity change, geometry-epoch change **even at an
|
epoch or buffer identity change, geometry-epoch change **even at an
|
||||||
unchanged cell total**, and detach. **An ordinary accepted `Up` must
|
unchanged cell total**, and detach. The last four apply to the legacy
|
||||||
clear the latch**, or a later invalidation synthesises a duplicate
|
and mapped families alike; generation advance exists only in the
|
||||||
release.
|
mapped family. **An ordinary accepted `Up(Left)` must clear the
|
||||||
|
latch**, or a later invalidation synthesises a duplicate release.
|
||||||
|
- **"The replacement frame" was not one producer signal.** A mapped
|
||||||
|
generation or panel/buffer replacement clears on atomic acceptance of
|
||||||
|
its valid frame; `Absent` clears when accepted; a local geometry
|
||||||
|
declaration and detach clear at their own transition, before any
|
||||||
|
replacement frame can be required. Invalid frame payloads and
|
||||||
|
same-identity repaints retain the latch.
|
||||||
|
- **Gesture liveness and click counting are different state.** The
|
||||||
|
accepted-gesture latch covers an accepted `Down(Left)` only and is
|
||||||
|
consumed by `Up(Left)` or loss of authority. `MouseClickState`
|
||||||
|
deliberately survives an ordinary `Up` for multi-click detection,
|
||||||
|
but its mapping identity must be cleared or re-keyed on every mapping
|
||||||
|
invalidation even when no gesture is held.
|
||||||
|
- **Routing controls had been written as effects.** This base can prove
|
||||||
|
legacy and mapped events reach the already-landed focus path; it
|
||||||
|
cannot prove selection, terminal reporting, drag continuation,
|
||||||
|
two-tick scrolling, or exhaustion cleanup before replay exists.
|
||||||
|
Those controls are split, and the effect halves are obligations of
|
||||||
|
the rebased replay lane rather than claims made by this slice.
|
||||||
|
- **Bilateral gating needed all four wrong-family legs.** The v25
|
||||||
|
refusal rows did not constrain a v24 session sending or accepting the
|
||||||
|
mapped variants. Same-crate peers can encode them regardless of what
|
||||||
|
they negotiated, so both inbound and outbound are now witnessed at
|
||||||
|
v24 and v25, with exact-family positive controls.
|
||||||
- **G9b's mutation was a valid implementation.** Keying dedupe by
|
- **G9b's mutation was a valid implementation.** Keying dedupe by
|
||||||
`(mapping_generation, coord)` is a correct design, so requiring it to
|
`(mapping_generation, coord)` is a correct design, so requiring it to
|
||||||
fail would have forbidden a good one. Replaced with two real defects.
|
fail would have forbidden a good one. Replaced with two real defects.
|
||||||
|
|
@ -1799,6 +1825,13 @@ introduced:
|
||||||
- Zero-generation rows added **in both directions**; G7 split into
|
- Zero-generation rows added **in both directions**; G7 split into
|
||||||
outbound and inbound legs; G2's grid and fold composites split;
|
outbound and inbound legs; G2's grid and fold composites split;
|
||||||
journey steps **5** and **8** named in §20.
|
journey steps **5** and **8** named in §20.
|
||||||
|
- **The folded closure pass made the remaining implicit contracts
|
||||||
|
executable in one matrix:** authenticated-session family authority;
|
||||||
|
a frontend generation high-water mark surviving `Absent`; exact pins
|
||||||
|
for the new variants as well as their previous finals; left-only and
|
||||||
|
fixed-domain terminal gestures; exact-once coincident invalidations;
|
||||||
|
independent click identity; the semantic/TUI boundary; and explicit
|
||||||
|
ownership of every route versus effect row (G0–G15).
|
||||||
|
|
||||||
**Previously, revision 15 — SUPERSEDED.**
|
**Previously, revision 15 — SUPERSEDED.**
|
||||||
Revision 15 answers review of 14, and four of its items reverse
|
Revision 15 answers review of 14, and four of its items reverse
|
||||||
|
|
@ -1848,6 +1881,15 @@ This is the one hole in the ladder: `buffer_id` catches replacement,
|
||||||
declaration race, and **nothing catches "the text under that cell
|
declaration race, and **nothing catches "the text under that cell
|
||||||
changed"**.
|
changed"**.
|
||||||
|
|
||||||
|
The hole exists only across the **semantic frontend's asynchronous
|
||||||
|
projection/inbound seam**. The TUI hit-tests and dispatches against its
|
||||||
|
current daemon state in one process; it neither receives `PanelFrame`
|
||||||
|
nor echoes a mapping generation. This slice adds no TUI token, gate or
|
||||||
|
gesture state, and the existing direct TUI panel effects remain a
|
||||||
|
regression control. Applying the mapped-family check inside the shared
|
||||||
|
replay body instead of at the semantic-session boundary would break
|
||||||
|
that control.
|
||||||
|
|
||||||
**Revision 12 accepted this as narrow. It is not.** A **foreign** edit
|
**Revision 12 accepted this as narrow. It is not.** A **foreign** edit
|
||||||
moves the mapping with `view_top` untouched; ticks, paging, folds,
|
moves the mapping with `view_top` untouched; ticks, paging, folds,
|
||||||
edits and reloads accumulate without bound; and the window lasts until
|
edits and reloads accumulate without bound; and the window lasts until
|
||||||
|
|
@ -1989,6 +2031,14 @@ future reader can see that nothing was reordered.
|
||||||
default-constructed or partially-initialised sender produces, and
|
default-constructed or partially-initialised sender produces, and
|
||||||
accepting it would let a peer opt out of the check by sending
|
accepting it would let a peer opt out of the check by sending
|
||||||
nothing. A live key starts at 1.
|
nothing. A live key starts at 1.
|
||||||
|
- It is **monotonic for the authenticated frontend session**. A
|
||||||
|
frontend accepts a valid generation above its retained high-water
|
||||||
|
mark (gaps are fine), and accepts an equal-generation repaint because
|
||||||
|
styling, focus and selection may legitimately change while the
|
||||||
|
mapping holds. It rejects a lower generation atomically. `Absent`
|
||||||
|
clears the presentation and pointer state but **retains the
|
||||||
|
high-water mark**; otherwise a delayed pre-`Absent` frame can roll
|
||||||
|
authority backward. Detach ends the session and its high-water mark.
|
||||||
- **`PANEL_MAPPING_MIN_VERSION = 25`**, beside the existing family
|
- **`PANEL_MAPPING_MIN_VERSION = 25`**, beside the existing family
|
||||||
constants, and the gate reads that constant rather than a literal.
|
constants, and the gate reads that constant rather than a literal.
|
||||||
|
|
||||||
|
|
@ -2006,6 +2056,14 @@ slice exists to close, reachable by omitting a field.
|
||||||
| **≤ v24** | `Present` | `PanelPointer` | `PanelPointer` | `Present` |
|
| **≤ v24** | `Present` | `PanelPointer` | `PanelPointer` | `Present` |
|
||||||
| **≥ v25** | `PresentMapped` | `PanelPointerMapped` **only** | `PanelPointerMapped` | `PresentMapped` **only** |
|
| **≥ v25** | `PresentMapped` | `PanelPointerMapped` **only** | `PanelPointerMapped` | `PresentMapped` **only** |
|
||||||
|
|
||||||
|
Both choices use the **authenticated session's negotiated version**.
|
||||||
|
Inbound code never derives the family gate from the payload's claimed
|
||||||
|
`frontend_id`; like every inbound variant, that field is untrusted and
|
||||||
|
is validated separately. Outbound projection uses the destination
|
||||||
|
session's negotiated version, not the crate's compile-time ability to
|
||||||
|
encode either discriminant. The family gate runs before focus,
|
||||||
|
controller, click-chain, gesture-latch or document mutation.
|
||||||
|
|
||||||
- A **≥ v25 session sending bare `PanelPointer` is REFUSED**, dropped
|
- A **≥ v25 session sending bare `PanelPointer` is REFUSED**, dropped
|
||||||
before any mutation, exactly as an out-of-epoch event is.
|
before any mutation, exactly as an out-of-epoch event is.
|
||||||
- A **≥ v25 frontend receiving legacy `Present` REJECTS it** rather
|
- A **≥ v25 frontend receiving legacy `Present` REJECTS it** rather
|
||||||
|
|
@ -2045,6 +2103,13 @@ row and column into the child's input stream, so a stale one aims an
|
||||||
application action at a cell the user never pointed at — the same
|
application action at a cell the user never pointed at — the same
|
||||||
hazard as a stale click, and it keeps the check.
|
hazard as a stale click, and it keeps the check.
|
||||||
|
|
||||||
|
The exemption skips **only generation equality**. It does not bypass
|
||||||
|
the authenticated family gate, the nonzero-generation requirement,
|
||||||
|
frontend/panel/geometry/buffer identity, coordinate bounds, or target
|
||||||
|
resolution. In particular, a zero-generation coordinate-free wheel is
|
||||||
|
still refused; zero means "no mapping identity was supplied", not "one
|
||||||
|
was supplied and has since gone stale".
|
||||||
|
|
||||||
**Witnesses: two ticks under ONE generation.** A document wheel and a
|
**Witnesses: two ticks under ONE generation.** A document wheel and a
|
||||||
non-reporting terminal wheel must each scroll **twice** when the second
|
non-reporting terminal wheel must each scroll **twice** when the second
|
||||||
event carries the generation the first invalidated. Without a
|
event carries the generation the first invalidated. Without a
|
||||||
|
|
@ -2071,20 +2136,37 @@ own `Up`**, and the daemon is never told anything — so the selection
|
||||||
stays armed and the child keeps holding its button. **The cancelling
|
stays armed and the child keeps holding its button. **The cancelling
|
||||||
event never arrives.**
|
event never arrives.**
|
||||||
|
|
||||||
So the trigger is the authoritative key advancing while a gesture is
|
So the daemon cancels at the transition that revokes authority, not at
|
||||||
accepted:
|
the next pointer event:
|
||||||
|
|
||||||
1. **Daemon, on losing gesture authority with a gesture accepted** —
|
| authority loss | daemon cancellation point | protocol families |
|
||||||
this runs whether or not any further event arrives:
|
|---|---|---|
|
||||||
- **document:** clear an empty selection, clear the click chain,
|
| mapping generation advances | after the mapping mutation and **before** publishing the next `PresentMapped` | mapped, ≥ v25 only |
|
||||||
apply no cursor move;
|
| `Absent` | **before** publishing `Absent` | legacy and mapped |
|
||||||
- **terminal:** deliver the child's **release**, at the last
|
| panel epoch or buffer identity changes | before publishing the successor `Present`/`PresentMapped` | legacy and mapped |
|
||||||
coordinate known valid, with the button and encoding the accepted
|
| geometry epoch changes | when the new frontend geometry declaration is accepted, even when its rows and columns equal the old declaration | legacy and mapped |
|
||||||
`Down` used.
|
| detach | in that frontend's teardown, before its pointer state is discarded | legacy and mapped |
|
||||||
2. **Producer: clear the local latch on receiving the replacement
|
|
||||||
frame, and send nothing.** The daemon has already cancelled, ahead
|
If an accepted left-button gesture is live, cancellation is taken
|
||||||
of emitting that frame, so the gesture is over before the frontend
|
**exactly once**:
|
||||||
hears about it.
|
|
||||||
|
- **document:** clear an empty selection, preserve an already-nonempty
|
||||||
|
region, and apply no cursor move;
|
||||||
|
- **terminal:** deliver the child's **release**, at the last coordinate
|
||||||
|
known valid, with the left button and encoding the accepted press
|
||||||
|
used.
|
||||||
|
|
||||||
|
The click chain is invalidated at the same authority transitions but is
|
||||||
|
**not conditional on a live gesture**; its lifetime is specified
|
||||||
|
separately below.
|
||||||
|
|
||||||
|
Authority losses may coincide. A changed-size geometry declaration, for
|
||||||
|
example, advances both `geometry_epoch` and the mapping generation;
|
||||||
|
buffer/panel replacement can also change the projected mapping. All
|
||||||
|
causes take the **same** accepted-gesture latch, so the first
|
||||||
|
cancellation settles it and later causes observe no live gesture.
|
||||||
|
Click-chain invalidation is likewise idempotent. Trigger ordering may
|
||||||
|
not create two releases or two selection cleanups.
|
||||||
|
|
||||||
**Revision 15 asked the producer to emit a cancellation tail or retain
|
**Revision 15 asked the producer to emit a cancellation tail or retain
|
||||||
the latch. Both are wrong, and the second is actively harmful.** A tail
|
the latch. Both are wrong, and the second is actively harmful.** A tail
|
||||||
|
|
@ -2095,13 +2177,31 @@ manufactures a `Drag` under the NEW generation with no accepted
|
||||||
`Down`** — precisely the orphan this whole section is about, created by
|
`Down`** — precisely the orphan this whole section is about, created by
|
||||||
the rule meant to avoid it.
|
the rule meant to avoid it.
|
||||||
|
|
||||||
**Ordering is what makes the simple rule safe.** Cancel, then emit. The
|
**There is no single producer-side "replacement frame" transition.**
|
||||||
frontend's arrival of the replacement frame *is* the cancellation
|
The producer clears its local held-gesture state at the first
|
||||||
signal, so no second channel is needed.
|
authoritative signal available for the cause:
|
||||||
|
|
||||||
**Witness:** `Down` → the key advances → the replacement frame lands →
|
| cause | producer reset point |
|
||||||
subsequent motion and the physical `Up` produce **no new drag and no
|
|---|---|
|
||||||
duplicate release**.
|
| mapped generation changes | atomically accepting the valid successor `PresentMapped` |
|
||||||
|
| panel epoch or buffer identity changes | atomically accepting the valid successor `Present`/`PresentMapped` |
|
||||||
|
| `Absent` | accepting `Absent` |
|
||||||
|
| geometry epoch changes | locally, when `next_geometry_declaration` advances the epoch (`pmacs-gpu/src/main.rs:6847`), **before** sending the declaration |
|
||||||
|
| detach | frontend teardown |
|
||||||
|
|
||||||
|
An invalid frame and a repaint with the same generation and identities
|
||||||
|
**do not clear** the producer latch. A local geometry declaration is
|
||||||
|
different: advancing the local epoch has already revoked the old grid,
|
||||||
|
so the producer clears before the daemon can accept or refuse the
|
||||||
|
declaration. For the daemon-authored frame cases, ordering is cancel
|
||||||
|
then emit; the frame's arrival is the producer's signal, so no second
|
||||||
|
channel is needed. For geometry and detach, the local transition is
|
||||||
|
itself the signal and no replacement frame is required.
|
||||||
|
|
||||||
|
After any reset, subsequent motion and the physical `Up` produce **no
|
||||||
|
new drag and no duplicate release**. The producer never emits a
|
||||||
|
cancellation tail and never carries the held state into the successor
|
||||||
|
identity.
|
||||||
|
|
||||||
##### The latch's full lifecycle
|
##### The latch's full lifecycle
|
||||||
|
|
||||||
|
|
@ -2113,7 +2213,7 @@ the rest to be inferred:
|
||||||
|---|---|
|
|---|---|
|
||||||
| **mapping generation advances** | the cells mean different bytes |
|
| **mapping generation advances** | the cells mean different bytes |
|
||||||
| **`Absent`** | the band the gesture belongs to is gone |
|
| **`Absent`** | the band the gesture belongs to is gone |
|
||||||
| **panel or buffer identity change** | the successor never saw the press |
|
| **panel epoch or buffer identity change** | the successor never saw the press |
|
||||||
| **geometry-epoch change** | a new declaration, **even when the cell total is unchanged** — the frontend re-declared, so the grid it hit-tested against is not the one in force |
|
| **geometry-epoch change** | a new declaration, **even when the cell total is unchanged** — the frontend re-declared, so the grid it hit-tested against is not the one in force |
|
||||||
| **detach** | there is no frontend left to finish the gesture |
|
| **detach** | there is no frontend left to finish the gesture |
|
||||||
|
|
||||||
|
|
@ -2124,14 +2224,40 @@ the same orphan race the replay lane's D1/D2 producer resets expose,
|
||||||
arriving from the daemon's side instead of the frontend's.
|
arriving from the daemon's side instead of the frontend's.
|
||||||
|
|
||||||
**The daemon needs an ACCEPTED-GESTURE LATCH** to do any of this, and
|
**The daemon needs an ACCEPTED-GESTURE LATCH** to do any of this, and
|
||||||
revision 14 assumed state that does not exist. Per frontend it records:
|
revision 14 assumed state that does not exist. The latch is common to
|
||||||
|
legacy `PanelPointer` and mapped `PanelPointerMapped`, because four of
|
||||||
|
the five authority losses are common. Per frontend it records:
|
||||||
|
|
||||||
- **whether a `Down` was ACCEPTED**, and for a terminal whether it
|
- whether a **`Down(Left)` was ACCEPTED**, and for a terminal whether
|
||||||
actually reached the child;
|
it actually reached the child;
|
||||||
- **the last valid coordinate**, the **button**, and the **encoding**
|
- the last valid coordinate and the encoding the child was told — a
|
||||||
the child was told — a release must match the press it terminates;
|
release must match the press it terminates;
|
||||||
- enough identity to know the gesture is still the one that began.
|
- enough identity to know the gesture is still the one that began.
|
||||||
|
|
||||||
|
It arms only **after** `Down(Left)` is accepted, updates its last valid
|
||||||
|
coordinate on an accepted drag, and is taken by either an ordinary
|
||||||
|
accepted `Up(Left)` or one authority-loss transition. `Move`, wheel,
|
||||||
|
context, and right/middle press events never arm it. `MouseKind` has
|
||||||
|
right and middle `Down` variants (`pmacs-protocol/src/message.rs:220`),
|
||||||
|
and the GPU panel path currently emits `Down(Right)` from the
|
||||||
|
press-only `apply_right_press` route
|
||||||
|
(`pmacs-gpu/src/main.rs:2997`, `:3029`). Treating every `Down` as held
|
||||||
|
would therefore manufacture a delayed release at the next
|
||||||
|
invalidation. If right/middle release semantics are added, they need
|
||||||
|
their own complete producer/replay contract rather than silently
|
||||||
|
borrowing this left-drag latch.
|
||||||
|
|
||||||
|
For a terminal, the accepted `Down(Left)` also fixes the gesture's
|
||||||
|
**domain** until it ends: child-reported, or local terminal selection.
|
||||||
|
Shift precedence and the child's reporting mode decide the domain at
|
||||||
|
the beginning; later modifier or mode changes do not switch a live
|
||||||
|
gesture into a path that never saw its Down. Thus a child-reported
|
||||||
|
gesture sends Drag/Up using the recorded reporting contract and exact
|
||||||
|
release encoding even if reporting turns off or Shift is pressed, and
|
||||||
|
a locally-started gesture stays local even if Shift is released or
|
||||||
|
reporting turns on. This does not freeze wheel precedence: a wheel is
|
||||||
|
self-contained and never arms the gesture latch.
|
||||||
|
|
||||||
Two consequences fall out of the latch and are ruled here:
|
Two consequences fall out of the latch and are ruled here:
|
||||||
|
|
||||||
- **A stale `Up` with no accepted `Down` is INERT.** It terminates
|
- **A stale `Up` with no accepted `Down` is INERT.** It terminates
|
||||||
|
|
@ -2142,10 +2268,53 @@ Two consequences fall out of the latch and are ruled here:
|
||||||
the claim is not taken back. Reclaiming would make a stale gesture
|
the claim is not taken back. Reclaiming would make a stale gesture
|
||||||
steal a live one's terminal.
|
steal a live one's terminal.
|
||||||
|
|
||||||
|
Detach has an additional ordering constraint: if the departing
|
||||||
|
frontend still owns the controller, its child's matching release is
|
||||||
|
delivered **before** normal detach teardown releases that claim and
|
||||||
|
discards the gesture record. If another frontend owns it, the release
|
||||||
|
still settles the recorded gesture without changing that newer claim.
|
||||||
|
Clearing controller/gesture state first would make the teardown path
|
||||||
|
look clean while leaving the child button down.
|
||||||
|
|
||||||
**A cancelled gesture is not a replayed one**: the release is delivered
|
**A cancelled gesture is not a replayed one**: the release is delivered
|
||||||
for liveness, and no selection or scroll effect is applied from the
|
for liveness, and no selection or scroll effect is applied from the
|
||||||
stale event.
|
stale event.
|
||||||
|
|
||||||
|
##### Click-chain identity is independent
|
||||||
|
|
||||||
|
`MouseClickState` (`src/editor.rs:352`) is **not** the
|
||||||
|
accepted-gesture latch. An ordinary
|
||||||
|
`Up(Left)` consumes the gesture latch, while click state intentionally
|
||||||
|
survives long enough to recognize a second click. Consequently, only
|
||||||
|
clearing click state from the cancellation path is insufficient: after
|
||||||
|
a completed click there may be no live gesture to cancel.
|
||||||
|
|
||||||
|
The click chain therefore carries, or is cleared against, the complete
|
||||||
|
mapping identity: frontend, target window and buffer, panel epoch,
|
||||||
|
geometry epoch, and — for mapped sessions — mapping generation. Every
|
||||||
|
authority transition in the table above invalidates it independently
|
||||||
|
of the gesture latch. A completed click followed by a foreign edit and
|
||||||
|
the successor mapped frame, then a `Down` at the same cell inside the
|
||||||
|
multi-click interval, is a **single** click under the new mapping, not
|
||||||
|
a double-click on unrelated text. The same rule holds for the common
|
||||||
|
legacy identity transitions even though v24 has no mapping generation.
|
||||||
|
|
||||||
|
This extends rather than replaces Q#M5 and the existing TUI event
|
||||||
|
lifetime: only an **unmodified `Down(Left)`** writes click state;
|
||||||
|
Shift-Down never enters the double-click chain. Drag, Move, wheel and
|
||||||
|
right/context gestures clear it, while ordinary `Up(Left)` and a
|
||||||
|
same-identity repaint preserve it. Mapping invalidation is an
|
||||||
|
additional clear, not permission to couple click state to every frame
|
||||||
|
or to the accepted-gesture latch.
|
||||||
|
|
||||||
|
Semantic click chains are **keyed per frontend**, then carry the target
|
||||||
|
identity above. Merely storing `frontend_id` inside one global slot
|
||||||
|
prevents a false cross-frontend double-click but lets B's click erase
|
||||||
|
A's legitimate chain. The adjacent `window_drag` comment already names
|
||||||
|
that exact defect and uses a `HashMap<FrontendId, ..>` because
|
||||||
|
concurrent frontend gestures are legal (`src/editor.rs:298`). Detach
|
||||||
|
removes only the departing frontend's entry.
|
||||||
|
|
||||||
### Motion must stay coalesced
|
### Motion must stay coalesced
|
||||||
|
|
||||||
`PanelPointerMapped` carrying `Move` or `Drag` **takes the same
|
`PanelPointerMapped` carrying `Move` or `Drag` **takes the same
|
||||||
|
|
@ -2166,6 +2335,8 @@ protecting.
|
||||||
| existing `FrontendEvent::PanelPointer` | retained, unchanged |
|
| existing `FrontendEvent::PanelPointer` | retained, unchanged |
|
||||||
| **new**: exact `FrontendEvent::TextInput` bytes | the previous-final `FrontendEvent`, appended by 1a and never pinned |
|
| **new**: exact `FrontendEvent::TextInput` bytes | the previous-final `FrontendEvent`, appended by 1a and never pinned |
|
||||||
| **new**: complete nested bytes of `InstanceMessage::PanelFrame(PanelFramePayload::Absent)` | the previous-final `PanelFramePayload` variant |
|
| **new**: complete nested bytes of `InstanceMessage::PanelFrame(PanelFramePayload::Absent)` | the previous-final `PanelFramePayload` variant |
|
||||||
|
| **new**: exact `FrontendEvent::PanelPointerMapped` bytes, encoded **and decoded**, with distinct values in every same-typed field | the new discriminant and its frozen field order; a same-type field swap still round-trips against itself, so round-trip alone is insufficient |
|
||||||
|
| **new**: complete nested bytes of `InstanceMessage::PanelFrame(PanelFramePayload::PresentMapped { .. })`, encoded **and decoded** | the new payload discriminant, nesting and `frame`-then-generation order |
|
||||||
|
|
||||||
**Correction: the `PanelPointer` pin DOES exist**, at
|
**Correction: the `PanelPointer` pin DOES exist**, at
|
||||||
`src/protocol.rs:1975`
|
`src/protocol.rs:1975`
|
||||||
|
|
@ -2173,27 +2344,66 @@ protecting.
|
||||||
14 said it could not be found. It is in the **root crate's** test
|
14 said it could not be found. It is in the **root crate's** test
|
||||||
module rather than under `pmacs-protocol/` or `tests/`, which is
|
module rather than under `pmacs-protocol/` or `tests/`, which is
|
||||||
exactly where the search did not look. `message.rs:524` was right and
|
exactly where the search did not look. `message.rs:524` was right and
|
||||||
the doubt was mine. The pin is **retained**, and the two new pins are
|
the doubt was mine. The pin is **retained**, and the four new pins are
|
||||||
added beside it.
|
added beside it. The previous-final pins catch an insertion on the
|
||||||
|
wrong side of the append boundary; the new-variant pins catch
|
||||||
|
reordering inside the variants themselves. Neither substitutes for the
|
||||||
|
other.
|
||||||
|
|
||||||
### Acceptance and mutation matrix
|
### Acceptance and mutation matrix
|
||||||
|
|
||||||
| # | row | mutation |
|
| # | row | mutation |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
| G0a | the existing `PanelPointer` pin and the new exact previous-final `TextInput` and nested `PanelFrame(Absent)` pins all remain byte-identical | insert a wedge before each protected previous-final variant → only the pin whose discriminant moved fails |
|
||||||
|
| G0b | exact encode **and decode** bytes for `PanelPointerMapped` and nested `PresentMapped`, using unequal values for every adjacent same-typed field | reorder two `u64` fields or reverse `frame`/generation → a self-consistent round-trip stays green but the exact pin fails |
|
||||||
| G1 | a **foreign** edit before the next render → the old generation is refused | never advance on content change → G1 passes a stale hit |
|
| G1 | a **foreign** edit before the next render → the old generation is refused | never advance on content change → G1 passes a stale hit |
|
||||||
| G2 | every **changing** entry moves the key, one row each — and the composites are **split**: `view_top`; `view_left`; grid **rows** and **columns** separately; **fold-map content** and the owning frontend's **`fold_projection` policy** separately; **wrap** and **gutter geometry** separately; buffer content; terminal **projected content** and **scrollback movement** separately | omit that one entry from the key |
|
| G2 | every **changing** entry moves the key, one row each — and the composites are **split**: `view_top`; `view_left`; grid **rows** and **columns** separately; **fold-map content** and the owning frontend's **`fold_projection` policy** separately; **wrap** and **gutter geometry** separately; buffer content; terminal **projected content** and **scrollback movement** separately | omit that one entry from the key |
|
||||||
| G3 | every **stable** entry leaves it unchanged, one row each — focus; styling; selection-only; absorbed cursor motion; and the terminal controls **style, title, bell, tab-stop, cursor-only** | include that one entry → drags cancel on a repaint |
|
| G3 | every **stable** entry leaves it unchanged, one row each — focus; styling; selection-only; absorbed cursor motion; and the terminal controls **style, title, bell, tab-stop, cursor-only** | include that one entry → drags cancel on a repaint |
|
||||||
| G4 | a **selection repaint** preserves the generation and an in-flight drag **continues** | as G3 |
|
| G4a | a **selection repaint** preserves the generation | as G3 |
|
||||||
| G5a | the key advancing with an accepted gesture live **raises cancellation** — the daemon acts without waiting for another event | make cancellation reactive to a refused event → nothing happens when the frame lands before the physical `Up` |
|
| G4b | after that repaint, an in-flight drag reaches real replay and **continues** | route only or clear the latch despite the stable generation → G4a stays green while the gesture dies |
|
||||||
| G5b | a stale `Up` with **no accepted `Down`** is **inert** | synthesise a release anyway → an unpressed button is released |
|
| G5a | **mapped generation**: the key advancing with an accepted left gesture live raises cancellation before the successor frame, without waiting for another pointer event | make cancellation reactive to a refused event → nothing happens when the frame lands before the physical `Up` |
|
||||||
| G5c | cancellation **does not reclaim** a controller another frontend has since taken | reclaim it → a stale gesture steals a live one's terminal |
|
| G5b | **table-driven common authority-loss matrix**: `Absent`, panel-epoch change, buffer replacement, same-size geometry-epoch change, and detach each cancel a live left gesture; every entry has legacy-v24 and mapped-v25 legs, and the rebased effect matrix applies each to a document and a reporting terminal | omit that transition, arm the latch only for the mapped family, or cancel only one target kind → its named leg leaves the gesture live |
|
||||||
| G6 | **v24 positive control** — a legacy session drives a panel gesture end to end | gate ≤ v24 off → old peers lose the panel |
|
| G5c | an ordinary accepted `Down(Left)` → `Up(Left)` consumes the latch; a later authority loss emits **no second** document cleanup or child release, with v24 same-size geometry and v25 generation legs | leave the latch armed after `Up` → the later transition duplicates the release |
|
||||||
| G7a | **v25 positive control, OUTBOUND** — a v25 peer receives `PresentMapped`, carrying a live generation | withhold `PresentMapped` from a v25 peer → the mapped frame never reaches it |
|
| G5d | a stale `Up` with **no accepted `Down`** is **inert** | synthesise a release anyway → an unpressed button is released |
|
||||||
| G7b | **v25 positive control, INBOUND** — a `PanelPointerMapped` with the current generation is **accepted and takes effect** | ignore the mapped variant inbound → every refusal row still passes while nothing works |
|
| G5e | cancellation **does not reclaim** a controller another frontend has since taken | reclaim it → a stale gesture steals a live one's terminal |
|
||||||
| G8a | a **bare `PanelPointer` from a ≥ v25 session is REFUSED** | accept it → the bypass returns inbound |
|
| G5f | **producer reset matrix**: mapped generation and panel/buffer replacement clear only on atomic acceptance of the valid successor frame; `Absent` clears on acceptance; same-size geometry clears at local declaration; detach clears in teardown; structurally invalid, stale, zero-generation and wrong-family frames plus same-identity repaints retain | funnel every reset through frame arrival → geometry/detach stay armed; clear before validation or on every repaint → the negative controls lose a valid drag |
|
||||||
| G8b | a **legacy `Present` at a ≥ v25 frontend is REJECTED** | paint it → the frontend hit-tests a band it cannot map |
|
| G5g | only an accepted **`Down(Left)`** arms cancellation; table-driven Move, wheel, context and right/middle controls followed by an authority loss emit no synthetic release | arm on every `Down` or every accepted pointer event → the press-only right path or another non-gesture event manufactures a delayed release |
|
||||||
|
| G5h | **click-chain identity is independent**: complete a click, then independently drive `Absent`→Present, panel-epoch change, buffer replacement, same-size geometry change, and detach→reattach with no live gesture; a same-cell click inside the interval is single. A foreign content edit plus successor generation is the additional mapped-v25 leg | clear/re-key `MouseClickState` only while cancelling a held gesture, or omit one identity → unrelated text is treated as the second/third click |
|
||||||
|
| G5i | reporting-terminal detach delivers the recorded child release before dropping the old gesture/controller state, then leaves no controller claim owned by the detached frontend; a newer owner's claim remains untouched | tear down controller or gesture state before cancellation → no release bytes; reclaim/release unconditionally → the newer owner is disturbed |
|
||||||
|
| G5j | document cancellation has two legs: Down with no accepted Drag clears the empty selection; Down plus accepted Drag preserves the nonempty region's exact anchor/cursor while ending the gesture | clear every selection → the user's completed range disappears; preserve every selection → the empty armed anchor captures later motion |
|
||||||
|
| G5k | **terminal gesture-domain matrix**: child-reported Down then (a) reporting off or (b) Shift held before Drag/Up still yields child Drag/release in the recorded encoding and no local selection; local Down via (c) Shift or (d) reporting-off then the condition reverses still finishes locally with no child tail | re-evaluate Shift/reporting independently on every event → the child keeps a button down or receives an Up for a Down it never saw |
|
||||||
|
| G5l | **click-chain stability control**: complete a click, accept a same-generation style/selection repaint with every identity unchanged, then click the same cell inside the interval → it remains the second click | clear click state on every frame or couple it to the ordinary-Up gesture-latch clear → multi-click never survives a harmless repaint/release |
|
||||||
|
| G5m | **coincident invalidations cancel once**: changed-size geometry (geometry epoch + mapping generation) and panel/buffer replacement that also changes the mapping each produce exactly one document cleanup/child release | keep per-cause live flags or inspect without taking the shared latch → one transition emits duplicate tails |
|
||||||
|
| G5n | **click event-lifetime table**: unmodified left Down writes; Shift-Down does not; Drag, Move, wheel and right/context clear; ordinary left Up preserves | write on Shift-Down or omit one clear arm → Q#M5 or the successor click is misclassified; clear on Up → every double-click becomes two singles |
|
||||||
|
| G5o | **per-frontend click ownership**: A click, B click, then A same-cell click inside A's interval still produces A's double-click, while detaching B removes only B's chain | keep one global `Option<MouseClickState>` or clear the whole map on detach → B erases A's valid sequence |
|
||||||
|
| G5p | **per-frontend gesture ownership**: A and B hold gestures on distinct panels; B's panel-epoch change or detach cancels B exactly once while A's next valid Drag still applies | keep one global accepted-gesture latch or clear the whole map on one frontend transition → B's lifecycle cancels or erases A |
|
||||||
|
| G6a | **v24 positive control, OUTBOUND** — a v24 peer receives exactly legacy `Present`, never `PresentMapped` | send the mapped family or both → an old negotiated session receives a discriminant it did not declare |
|
||||||
|
| G6b | **v24 routing control, INBOUND** — a current legacy `Down(Left)` reaches the semantic dispatcher and performs the already-landed focus activation | gate v24 off or drop after decoding → the old peer's event is mapped but inert |
|
||||||
|
| G6c | **v24 effect control after replay rebase** — the same event performs its document/terminal replay effect | keep only the pre-existing focus path → G6b stays green while replay is dead |
|
||||||
|
| G7a | **v25 positive control, OUTBOUND** — a v25 peer receives exactly `PresentMapped`, carrying a live generation, never legacy `Present` | withhold `PresentMapped`, send legacy, or send both → the mapped frame contract is absent or ambiguous |
|
||||||
|
| G7b | **v25 routing control, INBOUND** — a current `PanelPointerMapped` carrying `Down(Left)` reaches the semantic dispatcher and performs the already-landed focus activation | ignore the mapped variant inbound → every refusal row still passes while nothing routes |
|
||||||
|
| G7c | **v25 effect control after replay rebase** — that current mapped event performs its document/terminal replay effect | stop after focus → G7b stays green while replay is dead |
|
||||||
|
| G8a | a **bare `PanelPointer` from a ≥ v25 session is REFUSED before mutation** | accept it → the bypass returns inbound |
|
||||||
|
| G8b | a **legacy `Present` at a ≥ v25 frontend is REJECTED**, atomically retaining its mapped frame, generation and pointer state | paint it or clear state first → the frontend hit-tests a band it cannot map or loses a valid gesture |
|
||||||
|
| G8c | a **`PanelPointerMapped` from a ≤ v24 session is REFUSED before mutation**, even though a peer compiled from this crate can encode it | accept it → inbound negotiation is treated as a sender convention rather than a gate |
|
||||||
|
| G8d | a **`PresentMapped` at a ≤ v24 frontend is REJECTED**, retaining the previous legacy frame atomically | paint it → outbound negotiation is treated as a sender convention rather than a gate |
|
||||||
|
| G8e | **authenticated-session authority**: in both inbound wrong-family cases, claiming another frontend whose session negotiated the desired family still refuses | gate by the payload's `frontend_id` lookup → the forged cross-session claim bypasses negotiation |
|
||||||
| G9a | **daemon emission**: identical cells across a generation change are still **emitted** | dedupe daemon-side across the change → the second gesture is suppressed |
|
| G9a | **daemon emission**: identical cells across a generation change are still **emitted** | dedupe daemon-side across the change → the second gesture is suppressed |
|
||||||
| G9b | **frontend motion dedupe** suppresses a repeat within one generation and **re-arms across one** | (a) compare **only the cell**, never keying or resetting by generation → the first post-change motion is **eaten**; (b) reset on **every same-generation repaint** → repeated same-cell motion **returns** as pixel-rate traffic |
|
| G9b | **frontend motion dedupe** suppresses a repeat within one generation and **re-arms across one** | (a) compare **only the cell**, never keying or resetting by generation → the first post-change motion is **eaten**; (b) reset on **every same-generation repaint** → repeated same-cell motion **returns** as pixel-rate traffic |
|
||||||
|
| G9c | **frontend acceptance**: a byte-identical `PanelFrame` with a higher generation still updates the high-water/current generation and resets pointer state atomically; it may report no visual redraw | return early on frame equality before applying generation → G9a passes because the message arrived, but the producer keeps echoing stale authority |
|
||||||
|
| G10 | an **invalid** mapped frame retains the previous frame **and** its generation, atomically | update one without the other → a valid generation names a frame never shown |
|
||||||
|
| G10a | a **structurally valid `PresentMapped` carrying generation ZERO is REJECTED**, atomically — previous frame and generation both retained | accept zero → a sender that never initialised the field disables the check for the whole session |
|
||||||
|
| G10b | a **`PanelPointerMapped` carrying generation ZERO is REFUSED** before mutation, including a coordinate-free document and local-terminal wheel | apply the wheel exemption before the nonzero check → the inbound opt-out returns through the exempt path |
|
||||||
|
| G10c | mapped-frame generation is nondecreasing: a higher generation may skip values, an equal-generation style/selection repaint is accepted, and a lower generation is rejected atomically; after `PresentMapped(9)` → `Absent`, a valid `PresentMapped(8)` is still rejected | require only nonzero, or clear the high-water mark on `Absent` → a delayed frame rolls the producer's authority backward |
|
||||||
|
| G11a | **generation exhaustion fails CLOSED, and does not leave a zombie band**: the daemon publishes `Absent`, clears input authority, and latches exhaustion for the session | fail open → the hole returns at the boundary; refuse input only → a stale panel stays painted and permanently inert; omit the latch → the next frame resurrects it |
|
||||||
|
| G11b | exhaustion with an accepted left gesture also performs the ordinary `Absent` cancellation effects **before** publishing it | publish `Absent` and discard authority without replay cancellation → G11a stays green while the gesture is orphaned |
|
||||||
|
| G12a | one mapped generation carrying **two document wheel ticks** applies both window-level scrolls even though the first changes the mapping | check every mapped coordinate indiscriminately → only the first tick applies |
|
||||||
|
| G12b | one mapped generation carrying **two non-reporting terminal wheel ticks** applies both local-scrollback moves even though the first changes the view anchor | remove the terminal coordinate-free exemption → only the first tick applies |
|
||||||
|
| G13a | mapped `Move` and `Drag` retain their respective tail-coalescing behaviour, including replacement by the latest coordinates/generation without crossing an intervening lossless event | let the new variant fall through to the default → pixel-rate traffic fills the bounded queue |
|
||||||
|
| G13b | mapped press, release and all wheel kinds remain lossless and ordered | coalesce every mapped pointer → multi-click, release or wheel ticks silently disappear |
|
||||||
|
| G14a | version tripwire: `PROTOCOL_VERSION == 25`, `SUPPORTED_PROTOCOL_VERSIONS` reaches it, `PANEL_MAPPING_MIN_VERSION == 25`, and `ADVERTISED_PROTOCOL_VERSION == 20` remains pinned | bump only the wire constant or move the advertised baseline → self-handshake fails or the permanent baseline drifts |
|
||||||
|
| G14b | the supported set accepts every version through `PROTOCOL_VERSION`, rejects `PROTOCOL_VERSION + 1`, and the literal boundary routes v24 legacy versus v25 mapped | end the set at 24 or express the boundary arithmetically against a moving ceiling → the current wire is unsupported or a later bump silently moves this feature |
|
||||||
|
| G15 | **TUI structural control** — the existing direct panel click/drag/wheel paths keep their effects with no generation token | move the generation requirement into shared replay instead of the authenticated semantic boundary → local TUI input is refused |
|
||||||
|
|
||||||
**G9b's mutation in revision 15 was a valid implementation, not a
|
**G9b's mutation in revision 15 was a valid implementation, not a
|
||||||
defect.** Keying the dedupe by `(mapping_generation, coord)` preserves
|
defect.** Keying the dedupe by `(mapping_generation, coord)` preserves
|
||||||
|
|
@ -2201,26 +2411,32 @@ same-generation suppression *and* naturally admits the first motion
|
||||||
under a new generation — it is one correct way to satisfy the row, so
|
under a new generation — it is one correct way to satisfy the row, so
|
||||||
requiring it to fail would have forbidden a good design. The two
|
requiring it to fail would have forbidden a good design. The two
|
||||||
mutations above are actual defects, one in each direction.
|
mutations above are actual defects, one in each direction.
|
||||||
| G10 | an **invalid** mapped frame retains the previous frame **and** its generation, atomically | update one without the other → a valid generation names a frame never shown |
|
|
||||||
| G10a | a **structurally valid `PresentMapped` carrying generation ZERO is REJECTED**, atomically — previous frame and generation both retained | accept zero → a sender that never initialised the field disables the check for the whole session |
|
|
||||||
| G10b | a **`PanelPointerMapped` carrying generation ZERO is REFUSED** before mutation | accept zero → the inbound half of the same opt-out |
|
|
||||||
|
|
||||||
G10a and G10b are **independent rows in opposite directions**, not one
|
G10a and G10b are **independent rows in opposite directions**, not one
|
||||||
row seen twice: zero can arrive from either peer, and G10's atomicity
|
row seen twice: zero can arrive from either peer, and G10's atomicity
|
||||||
row exercises neither.
|
row exercises neither.
|
||||||
| G11 | **generation exhaustion fails CLOSED, and does not leave a zombie band**: the daemon publishes **`Absent`**, clears input authority, **cancels any accepted gesture**, and **latches** exhaustion for the session | fail open → the hole returns at the boundary; refuse input only → a stale panel stays painted and permanently inert, with no signal to the user; omit the latch → the next frame resurrects it |
|
|
||||||
|
|
||||||
G2 and G3 are enumerated per entry deliberately: one row asserting "the
|
G2 and G3 are enumerated per entry deliberately: one row asserting "the
|
||||||
generation changed" cannot show **which** input moved it, and a key
|
generation changed" cannot show **which** input moved it, and a key
|
||||||
that ignores `view_left` passes every row that only scrolls
|
that ignores `view_left` passes every row that only scrolls
|
||||||
vertically.
|
vertically.
|
||||||
|
|
||||||
|
The mutation column states each row's **minimum required bite**, not a
|
||||||
|
prediction that no legitimate dependent can fail. Alternatives `(a)`,
|
||||||
|
`(b)`, or distinct omitted table arms are separate mutations; one
|
||||||
|
mega-mutation cannot establish their individual coverage. Record the
|
||||||
|
actual bite graph only after each compiling mutant runs. A proposed
|
||||||
|
mutation that is itself a valid implementation is replaced, as G9b
|
||||||
|
was, rather than used to reject the implementation.
|
||||||
|
|
||||||
### What this slice can witness, and what the replay lane owes
|
### What this slice can witness, and what the replay lane owes
|
||||||
|
|
||||||
**G5's EFFECTS cannot be proven on this branch, and revision 14 claimed
|
**Gesture EFFECTS cannot be proven on this branch, and earlier
|
||||||
them anyway.** Cancellation's observable outcomes — the producer latch,
|
revisions claimed some anyway.** Cancellation's observable outcomes —
|
||||||
the panel's selection, the child's release — are all reached through
|
the complete producer latch, the panel's selection, the click chain and
|
||||||
**panel replay, which does not exist at this base**.
|
the child's release — plus drag continuation, replay positive controls
|
||||||
|
and two-tick scrolling are all reached through **panel replay, which
|
||||||
|
does not exist at this base**.
|
||||||
`gesture_last_content_cell` and the document/terminal replay live on
|
`gesture_last_content_cell` and the document/terminal replay live on
|
||||||
`panel-pointer-replay` (`pmacs-gpu/src/main.rs:2143` there); the same
|
`panel-pointer-replay` (`pmacs-gpu/src/main.rs:2143` there); the same
|
||||||
struct here (`:2124`) has no such field, and
|
struct here (`:2124`) has no such field, and
|
||||||
|
|
@ -2231,17 +2447,18 @@ whoever runs the tests:
|
||||||
|
|
||||||
| owned by **this slice** | owed by the **rebased replay lane** |
|
| owned by **this slice** | owed by the **rebased replay lane** |
|
||||||
|---|---|
|
|---|---|
|
||||||
| the wire shapes, positions and field order | G5's document effects — empty selection cleared, click chain cleared |
|
| wire shapes, positions, pins and field order (G0), plus the bidirectional family gate (G6a–b, G7a–b, G8) | document cancellation effect — empty selection cleared without moving point |
|
||||||
| the family gate, both directions (G6–G8) | G5's terminal effect — the child's release, with matching button and encoding |
|
| authoritative-key changing/stable inputs (G1–G4a) and the **mapping-generation** authority-loss signal before frame emission (G5a) | real stable-generation drag continuation, G5a's effects, and the v24/v25 replay-effect controls (G4b, G6c, G7c) |
|
||||||
| the authoritative key: what moves it, what does not (G1–G4) | G5b's inert stale `Up` |
|
| mapped-frame atomicity and generation-aware producer state (the mapped-frame legs of G5f) | complete producer reset lifecycle, including the geometry, `Absent`, identity and detach paths whose fields are introduced by replay (G5f) |
|
||||||
| the wheel exemptions and their two-tick rows | G5c's controller non-reclaim |
|
| motion coalescing, dedupe, invalid-frame atomicity, zero rejection, exhaustion state, version fallout and the TUI structural control (G9–G11a, G13–G15) | every common legacy/mapped cancellation transition and real gesture/click lifecycle (G5b–e, G5g, G5i–p), exhaustion cancellation (G11b), and both two-tick wheel effects (G12) |
|
||||||
| **G5a** — that the key advancing RAISES cancellation, observed as the daemon-side signal | the end-to-end effect witnesses for that signal |
|
|
||||||
| G9–G11 | — |
|
|
||||||
|
|
||||||
**G5a stays here on purpose.** The trigger is this slice's rule; only
|
Only the **new mapping-generation** transition stays here on purpose,
|
||||||
its consequences need replay. Moving the whole row out would leave the
|
and its classification does **not** discharge the effect row. The
|
||||||
proactive-cancellation ruling — the one that fixes revision 14's race —
|
common invalidations do not get a dead classification helper on this
|
||||||
with no witness in the slice that introduces it.
|
base; replay owns them where accepted gesture state exists. The rebased
|
||||||
|
lane must run the whole G5 matrix through real document and terminal
|
||||||
|
call sites. This is the same producer-emits/receiver-discards blind
|
||||||
|
spot that forced the replay prerequisite in the first place.
|
||||||
|
|
||||||
### Coherence impact (`COHERENCE.md` §20)
|
### Coherence impact (`COHERENCE.md` §20)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue