docs(framing): bottom-panel revision 9 --- Q#BP-R2 overruled, and R-c2 stops fighting a tested contract
Answers review of revision 8. Still framing only; no implementation. Both items reverse a decision revision 8 made. **Q#BP-R2 IS OVERRULED: a terminal-chrome wheel is CONSUMED, not clamped.** I argued clamping on consistency grounds and missed that SGR wheel input is COORDINATE-BEARING: `encode_mouse` takes a `coord` and writes `coord.col + 1` / `coord.row + 1` into the emitted sequence (`src/terminal/input.rs:102`, `:146`) --- which A4, added one revision earlier, pins exactly. Clamping therefore fabricates a hit on the terminal's final content row, and an application that routes wheel input by position acts on a cell the user never pointed at. The consistency I was buying is cosmetic; the price is a synthetic coordinate handed to a program as real. The asymmetry with `Up` is the part worth keeping in the record: `Up` must be normalized because an unterminated gesture HANGS --- the daemon holds a button down forever --- while a wheel tick is self-contained and dropping one strands nothing. Liveness is what earns normalization, and the wheel has no liveness obligation. Consumed, and never fallen through to the document; TUI parity restored. **R-c2 WOULD HAVE BROKEN A TESTED GUARANTEE.** Revision 8 said to retain the `Down` cell in `last_pointer_cell`. That field is cleared on press deliberately, and a live test says why: "the first drag after a press must reach the daemon even at the cell the press landed on" (`pmacs-gpu/src/main.rs:19841`). Storing `Down` there makes the press's own cell the dedupe baseline and suppresses exactly that `Drag`. Ruled: a separate `gesture_last_content_cell`. The two fields have different jobs and conflating them was the error --- `last_pointer_cell` answers "is this motion worth sending?", the new one answers "where did this gesture last legitimately point?" --- different lifetimes, different resets. Written on arm and on each accepted content motion; reset on release and on BOTH identity changes; never consulted by `panel_motion_is_new`. A second mutation guards the separation itself: point the dedupe at the new field and the existing first-`Drag`-after-press assertion must fail. The alternative --- ruling `Down` the dedupe baseline and retiring the guarantee --- is recorded as considered and rejected. It would need a mutation showing the first same-cell `Drag` is redundant, and it is not obviously so, since that event is what establishes the daemon's drag state. **And the crossing table follows the choice.** Revision 8 promised a content-originated `Drag` over chrome would be sent. It should not be: once normalized, its coordinate is usually the one already reported and the ordinary dedupe suppresses it, correctly --- promising delivery would oblige the producer to defeat its own dedupe for a daemon state that is identical either way. `Up` is the load-bearing crossing event and the only one promised unconditionally. Gates: all nine green under `env -u TMPDIR`, log 20260814T123502Z. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
4833a73dfe
commit
7d6b6da366
|
|
@ -281,7 +281,7 @@ from #171 and #215 — the correction the 1b lane missed, honoured here.
|
||||||
**`githubsucks/panel-pointer-replay` is the authoritative tip** (the
|
**`githubsucks/panel-pointer-replay` is the authoritative tip** (the
|
||||||
ref, not a SHA). Recover with
|
ref, not a SHA). Recover with
|
||||||
`git fetch githubsucks && git checkout panel-pointer-replay`.
|
`git fetch githubsucks && git checkout panel-pointer-replay`.
|
||||||
- **No PR yet. Checkpoint: framing revision 8 (§5a) AWAITING APPROVAL;
|
- **No PR yet. Checkpoint: framing revision 9 (§5a) AWAITING APPROVAL;
|
||||||
NO IMPLEMENTATION WRITTEN.** Commit one was the ground-truth
|
NO IMPLEMENTATION WRITTEN.** Commit one was the ground-truth
|
||||||
re-measurement; 6 added the four replay edges; **7 answers review of
|
re-measurement; 6 added the four replay edges; **7 answers review of
|
||||||
6; **8 answers review of 7** — R-c is target × gesture-ORIGIN
|
6; **8 answers review of 7** — R-c is target × gesture-ORIGIN
|
||||||
|
|
@ -290,7 +290,16 @@ from #171 and #215 — the correction the 1b lane missed, honoured here.
|
||||||
positive SGR controls, and four witness seams are tightened. **New
|
positive SGR controls, and four witness seams are tightened. **New
|
||||||
ruling Q#BP-R2**: a chrome wheel over a terminal panel clamps into
|
ruling Q#BP-R2**: a chrome wheel over a terminal panel clamps into
|
||||||
content rather than dropping — a deliberate divergence from the TUI,
|
content rather than dropping — a deliberate divergence from the TUI,
|
||||||
flagged for overrule.
|
flagged for overrule — **and overruled in 9**.
|
||||||
|
**Revision 9** reverses two of 8's decisions: a terminal-chrome wheel
|
||||||
|
is **CONSUMED, not clamped** (SGR wheel input is coordinate-bearing,
|
||||||
|
so clamping fabricates a hit the user never made, and a wheel has no
|
||||||
|
liveness obligation), and the `Down`-cell fallback lives in a
|
||||||
|
**separate `gesture_last_content_cell`** rather than
|
||||||
|
`last_pointer_cell`, which is cleared on press precisely so the first
|
||||||
|
same-cell `Drag` reaches the daemon (`pmacs-gpu/src/main.rs:19841`).
|
||||||
|
**`Up` is the only crossing event promised unconditionally**; a
|
||||||
|
crossing `Drag` is normalized and then deduped.
|
||||||
- **Why this lane exists.** `PanelPointer` **replays nothing**:
|
- **Why this lane exists.** `PanelPointer` **replays nothing**:
|
||||||
`dispatch_semantic_panel_pointer` (`src/editor.rs:2674`) validates,
|
`dispatch_semantic_panel_pointer` (`src/editor.rs:2674`) validates,
|
||||||
focuses, returns. A panel wheel is dead on both axes and so is every
|
focuses, returns. A panel wheel is dead on both axes and so is every
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,31 @@ protocol v20, 2026-07-24. Amended by the pre-implementation dependency
|
||||||
verification in §0.6: the folding dependency is cleared, and one geometry
|
verification in §0.6: the folding dependency is cleared, and one geometry
|
||||||
caller-census error is corrected.**
|
caller-census error is corrected.**
|
||||||
|
|
||||||
**Revision 8 — 2026-08-14, AWAITING APPROVAL.** Answers review of 7:
|
**Revision 9 — 2026-08-14, AWAITING APPROVAL.** Answers review of 8.
|
||||||
|
Both items reverse a decision revision 8 made:
|
||||||
|
|
||||||
|
- **Q#BP-R2 is OVERRULED — a terminal-chrome wheel is CONSUMED, not
|
||||||
|
clamped.** SGR wheel input is **coordinate-bearing**: `encode_mouse`
|
||||||
|
writes `coord.col + 1` / `coord.row + 1` into the sequence
|
||||||
|
(`src/terminal/input.rs:102`, `:146`), which A4 pins exactly. Clamping
|
||||||
|
**fabricates a hit on the final content row**, so a
|
||||||
|
position-routing application acts on a cell the user never pointed
|
||||||
|
at. And unlike `Up`, a wheel has **no liveness obligation** —
|
||||||
|
dropping one strands nothing. Consumed, never fallen through to the
|
||||||
|
document; TUI parity restored.
|
||||||
|
- **R-c2's field choice is corrected: a SEPARATE
|
||||||
|
`gesture_last_content_cell`.** Revision 8 said to retain the `Down`
|
||||||
|
cell in `last_pointer_cell`, which would break a **tested
|
||||||
|
guarantee** — that field is cleared on press precisely so the first
|
||||||
|
same-cell `Drag` reaches the daemon
|
||||||
|
(`pmacs-gpu/src/main.rs:19841`). Dedupe baseline and termination
|
||||||
|
fallback are different jobs with different lifetimes.
|
||||||
|
- **The crossing table follows:** a content-originated `Drag` over
|
||||||
|
chrome is **normalized and then subject to the ordinary dedupe**, not
|
||||||
|
promised as sent. **`Up` is the load-bearing crossing event** and the
|
||||||
|
only one promised unconditionally.
|
||||||
|
|
||||||
|
**Previously, revision 8 — SUPERSEDED.** Answered review of 7:
|
||||||
|
|
||||||
- **R-c is target × gesture-ORIGIN, not kind alone.** The TUI's
|
- **R-c is target × gesture-ORIGIN, not kind alone.** The TUI's
|
||||||
per-kind rule is **document-only** — for terminals it rejects every
|
per-kind rule is **document-only** — for terminals it rejects every
|
||||||
|
|
@ -2113,33 +2137,49 @@ instead of behaving as a content wheel does.
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **document** | `Down(Left)`, `Down(Right)` | do not arm, do not send | reserved — drop |
|
| **document** | `Down(Left)`, `Down(Right)` | do not arm, do not send | reserved — drop |
|
||||||
| **document** | `Drag(Left)`, chrome-originated | not sent (never armed) | reserved — drop |
|
| **document** | `Drag(Left)`, chrome-originated | not sent (never armed) | reserved — drop |
|
||||||
| **document** | `Drag(Left)`, **content-originated** | **sent** — the gesture is live | **process at the last valid CONTENT coordinate** |
|
| **document** | `Drag(Left)`, **content-originated** | normalized to the last valid CONTENT cell, then **subject to the ordinary dedupe** | process when it arrives |
|
||||||
| **document** | `Up(Left)` | **send** | **process** — terminates the gesture |
|
| **document** | `Up(Left)` | **send** | **process** — terminates the gesture |
|
||||||
| **document** | wheel | **send** | **process** — scrolls the panel |
|
| **document** | wheel | **send** | **process** — scrolls the panel |
|
||||||
| **terminal** | any kind, chrome-**originated** | do not arm, do not send | drop, matching the TUI |
|
| **terminal** | any kind, chrome-**originated** | do not arm, do not send | drop, matching the TUI |
|
||||||
| **terminal** | `Drag`/`Up`, **content-originated**, now over chrome | **send** | **replay at the last valid CONTENT coordinate** — never the chrome row |
|
| **terminal** | `Drag`, content-originated, now over chrome | normalized, then **subject to the ordinary dedupe** | process when it arrives |
|
||||||
| **terminal** | wheel over chrome | **send, clamped to content** | as a content wheel: reported when eligible, else local scrollback |
|
| **terminal** | `Up`, content-originated, now over chrome | **always sent** | **replay at the last valid CONTENT coordinate** — never the chrome row |
|
||||||
|
| **terminal** | wheel over chrome | **do not send — consume** | n/a; never falls through to the document (Q#BP-R2) |
|
||||||
|
|
||||||
**A content-originated gesture terminates normally, at its last valid
|
**`Up` is the load-bearing crossing event, and the only one promised
|
||||||
content coordinate.** That is the whole of the crossing rule, and it is
|
unconditionally.** A content-originated gesture terminates at its last
|
||||||
what keeps a reporting child from being told about a row it does not
|
valid content coordinate — that is what keeps a reporting child from
|
||||||
own while still receiving its release.
|
being told about a row it does not own while still receiving its
|
||||||
|
release.
|
||||||
|
|
||||||
#### Q#BP-R2 — a chrome wheel over a TERMINAL panel: clamp, or drop? **RULED: clamp**
|
**A crossing `Drag` is NOT promised as sent.** Once normalized, its
|
||||||
|
coordinate is frequently the one already reported, and the ordinary
|
||||||
|
motion dedupe suppresses it — correctly. Promising delivery would
|
||||||
|
oblige the producer to defeat its own dedupe for no gain: the daemon's
|
||||||
|
state after a suppressed same-cell `Drag` is identical.
|
||||||
|
|
||||||
The one product call inside R-c, flagged because it **diverges from the
|
#### Q#BP-R2 — a chrome wheel over a TERMINAL panel **RULED: CONSUME, do not clamp**
|
||||||
TUI deliberately**. The TUI drops terminal mode-line wheels; this rules
|
|
||||||
that a panel clamps them into content instead.
|
|
||||||
|
|
||||||
The reason is that the panel's mode line is not a window's mode line
|
Revision 8 ruled *clamp*, on a consistency argument. **That was wrong,
|
||||||
among many — it is **the band's own chrome**, one row, and the document
|
and the reason is that SGR wheel input is COORDINATE-BEARING.**
|
||||||
panel already scrolls on a chrome wheel (the TUI does not guard the
|
`encode_mouse` takes a `coord` and writes `coord.col + 1` and
|
||||||
wheel for documents). Dropping for terminals would make **the same
|
`coord.row + 1` into the sequence (`src/terminal/input.rs:102`,
|
||||||
pixel behave differently depending on the buffer kind currently shown
|
`:146`–`:147`) — which A4 now pins exactly. **Clamping would fabricate
|
||||||
there**, which is not a distinction a user is holding in mind while
|
a hit on the terminal's final content row**, so an application that
|
||||||
scrolling. **Overrule this if you would rather have TUI parity**; it is
|
routes wheel input by position could act on a cell the user never
|
||||||
a one-line difference in the clamp and nothing else in R-c depends on
|
pointed at. The consistency gain is cosmetic; the cost is a synthetic
|
||||||
it.
|
coordinate delivered to a program as though it were real.
|
||||||
|
|
||||||
|
**And the wheel carries no liveness obligation.** `Up` must be
|
||||||
|
normalized because a gesture left un-terminated hangs — the daemon
|
||||||
|
holds a button down forever. A wheel tick is self-contained: dropping
|
||||||
|
one ends nothing and strands nothing.
|
||||||
|
|
||||||
|
**Ruling: a wheel over a terminal panel's chrome is CONSUMED** — not
|
||||||
|
reported, not scrolled locally, and **not fallen through to the
|
||||||
|
document**. The band owns the pixel either way. This also restores TUI
|
||||||
|
parity, which revision 8 traded away for the weaker argument.
|
||||||
|
|
||||||
|
Document chrome is unchanged: a wheel there still scrolls the panel.
|
||||||
|
|
||||||
**Both crossings need witnesses, and they fail in opposite
|
**Both crossings need witnesses, and they fail in opposite
|
||||||
directions:**
|
directions:**
|
||||||
|
|
@ -2170,13 +2210,52 @@ by a release over chrome or outside the band, with **no intervening
|
||||||
motion**, has no coordinate to fall back to, so the `Up` is either
|
motion**, has no coordinate to fall back to, so the `Up` is either
|
||||||
dropped or sent with nothing.
|
dropped or sent with nothing.
|
||||||
|
|
||||||
**Retain the `Down` cell when arming.** Row: `Down` in content →
|
**Revision 8 said "retain the `Down` cell in that field". That would
|
||||||
release over chrome, **no intervening motion** → the `Up` carries the
|
break a tested guarantee.** `last_pointer_cell` is cleared on press
|
||||||
**`Down` cell's** coordinate. *Mutation: keep clearing the cell on arm
|
*deliberately*, and a live test says why:
|
||||||
— the release has no coordinate.* **For a reporting terminal the row
|
|
||||||
asserts the exact child release BYTES**, not merely that the frontend
|
> *"A press or release re-arms it: the first drag after a press must
|
||||||
latch cleared: a latch that clears while the child never hears the
|
> reach the daemon even at the cell the press landed on."*
|
||||||
release is the failure this row exists for.
|
> (`pmacs-gpu/src/main.rs:19841`–`:19844`)
|
||||||
|
|
||||||
|
Storing the `Down` cell there would make the press's own cell the
|
||||||
|
dedupe baseline and **suppress that first `Drag`**.
|
||||||
|
|
||||||
|
**Ruled: a SEPARATE field, `gesture_last_content_cell`.** The two have
|
||||||
|
different jobs and conflating them was the error —
|
||||||
|
`last_pointer_cell` is a **wire dedupe baseline**, answering *"is this
|
||||||
|
motion worth sending?"*; the new field is a **termination fallback**,
|
||||||
|
answering *"where did this gesture last legitimately point?"*. They
|
||||||
|
have different lifetimes and different reset rules, so one field cannot
|
||||||
|
serve both without one job corrupting the other.
|
||||||
|
|
||||||
|
- **Written** on arm (the `Down` cell) and on every accepted content
|
||||||
|
motion.
|
||||||
|
- **Reset** alongside the rest of the gesture state: on release, and on
|
||||||
|
**both** identity changes (R-d's D1 and D2).
|
||||||
|
- **Never consulted** by `panel_motion_is_new`, which keeps its own
|
||||||
|
baseline and its existing behaviour unchanged.
|
||||||
|
|
||||||
|
*The alternative was considered and rejected:* ruling that `Down`
|
||||||
|
becomes the dedupe baseline, retiring the guarantee and its test. It
|
||||||
|
would need a justified mutation showing the first same-cell `Drag` is
|
||||||
|
redundant, and it is not obviously so — the daemon's drag state is
|
||||||
|
established by that event. **Preserving a tested contract beats
|
||||||
|
retiring one to save a field.**
|
||||||
|
|
||||||
|
Row: `Down` in content → release over chrome, **no intervening
|
||||||
|
motion** → the `Up` carries the **`Down` cell's** coordinate.
|
||||||
|
*Mutation: reset `gesture_last_content_cell` on arm instead of writing
|
||||||
|
the `Down` cell — the release has no coordinate.* **For a reporting
|
||||||
|
terminal the row asserts the exact child release BYTES**, not merely
|
||||||
|
that the frontend latch cleared: a latch that clears while the child
|
||||||
|
never hears the release is the failure this row exists for.
|
||||||
|
|
||||||
|
**A second mutation guards the separation itself:** make
|
||||||
|
`panel_motion_is_new` consult `gesture_last_content_cell`. The existing
|
||||||
|
first-`Drag`-after-press assertion (`:19841`) must fail — which is what
|
||||||
|
proves the new field did not quietly become the dedupe baseline after
|
||||||
|
all.
|
||||||
|
|
||||||
#### R-d. Panel replacement leaves the frontend's gesture latch armed
|
#### R-d. Panel replacement leaves the frontend's gesture latch armed
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue