docs(framing): bottom-panel revision 10 --- the ruling was on the wrong side of the seam
Answers review of revision 9. Still framing only; no implementation.
Q#BP-R2's outcome is unchanged; its ENFORCEMENT POINT moves.
**THE GPU CANNOT KNOW WHETHER A PANEL HOLDS A TERMINAL.** `PanelFrame`
carries `buffer_id`, both epochs, `size`, `cells`, `cursor` and
`focused` --- and no target-kind discriminator
(`pmacs-protocol/src/panel.rs:73`). The panel is an OPAQUE CELL
PROJECTION to the frontend. `state.terminal`
(`pmacs-gpu/src/main.rs:1964`) cannot answer either: it describes the
primary full-window terminal, not the side-window.
So revision 9's producer rule --- "terminal chrome wheel: do not send"
--- was unimplementable without a new wire field, and this lane is
explicitly non-protocol-bearing. I wrote a rule the frontend has no
information to obey.
The correction is structural, not cosmetic. **Every producer rule is
now target-blind**, and the table is split by SEAM SIDE rather than by
target:
producer claims the chrome wheel and sends `PanelPointer` with its
valid frame coordinate, for EVERY panel
daemon after validation and resolving the side window ---
document panel -> `scroll_window`;
terminal panel -> CONSUME, producing no child bytes, no
local scrollback, and no document fallthrough
The other producer rules collapse safely because both targets want the
same behaviour there: a chrome press is reserved by documents and
dropped by terminals --- both drop --- and a crossing `Up` must arrive
for either, to terminate a selection or deliver a release. The wheel
was the sole divergence, which is exactly why it was the rule that
could not survive on the producer side.
The chrome coordinate does cross the wire now, and that is fine: it is
a valid frame cell, coord validation accepts it, `scroll_window` is a
window-level move that never reads it, and the terminal branch never
forwards it to a child. Nothing is fabricated and nothing reaches an
application --- which was the whole objection to clamping.
**Witness: ONE frontend across a document->terminal replacement.**
Document chrome wheel scrolls the panel; after replacement the terminal
chrome wheel changes nothing. Doing it in one frontend across a
replacement is what makes it a control rather than two unrelated
observations --- geometry, pointer position and producer path are
identical, and only the target differs. Mutation: let the terminal
branch call `apply_terminal_gesture`, and the chrome coordinate fails
its reporting bounds check, falls into the local branch, and the row
catches the accidental local scrollback that revision 8's clamp would
have shipped on purpose.
Gates: all nine green under `env -u TMPDIR`, log 20260814T142336Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
7d6b6da366
commit
ec127fc8fa
|
|
@ -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 9 (§5a) AWAITING APPROVAL;
|
- **No PR yet. Checkpoint: framing revision 10 (§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
|
||||||
|
|
@ -300,6 +300,16 @@ from #171 and #215 — the correction the 1b lane missed, honoured here.
|
||||||
same-cell `Drag` reaches the daemon (`pmacs-gpu/src/main.rs:19841`).
|
same-cell `Drag` reaches the daemon (`pmacs-gpu/src/main.rs:19841`).
|
||||||
**`Up` is the only crossing event promised unconditionally**; a
|
**`Up` is the only crossing event promised unconditionally**; a
|
||||||
crossing `Drag` is normalized and then deduped.
|
crossing `Drag` is normalized and then deduped.
|
||||||
|
**Revision 10** keeps Q#BP-R2's outcome and moves its **enforcement
|
||||||
|
point**: the GPU **cannot know a panel holds a terminal** —
|
||||||
|
`PanelFrame` has no target-kind field
|
||||||
|
(`pmacs-protocol/src/panel.rs:73`) and `state.terminal` is the
|
||||||
|
primary full-window terminal — so a producer-side rule needed a new
|
||||||
|
wire field this lane must not add. **The producer is target-blind**
|
||||||
|
and sends the chrome wheel for every panel; **the daemon** resolves
|
||||||
|
the side window and decides: document → `scroll_window`, terminal →
|
||||||
|
consume. Witness: one frontend across a document→terminal
|
||||||
|
replacement.
|
||||||
- **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,8 +7,29 @@ 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 9 — 2026-08-14, AWAITING APPROVAL.** Answers review of 8.
|
**Revision 10 — 2026-08-14, AWAITING APPROVAL.** Answers review of 9,
|
||||||
Both items reverse a decision revision 8 made:
|
which put a correct ruling **on the wrong side of the seam**.
|
||||||
|
|
||||||
|
**The GPU cannot know whether a panel holds a terminal.** `PanelFrame`
|
||||||
|
carries `buffer_id`, both epochs, `size`, `cells`, `cursor` and
|
||||||
|
`focused` and **no target-kind discriminator**
|
||||||
|
(`pmacs-protocol/src/panel.rs:73`); `state.terminal`
|
||||||
|
(`pmacs-gpu/src/main.rs:1964`) is the **primary full-window** terminal,
|
||||||
|
not the side-window projection. So revision 9's producer rule
|
||||||
|
*"terminal chrome wheel: do not send"* was unimplementable without a
|
||||||
|
new wire field, which this lane must not add.
|
||||||
|
|
||||||
|
**Q#BP-R2's OUTCOME is unchanged; its ENFORCEMENT POINT moves.** The
|
||||||
|
producer is now target-blind throughout — it claims the chrome wheel
|
||||||
|
and sends it for every panel — and the daemon, which resolves the side
|
||||||
|
window and knows the buffer kind, decides: document → `scroll_window`;
|
||||||
|
terminal → **consume**, with no child bytes, no local scrollback and no
|
||||||
|
document fallthrough. The producer/receiver tables are split by seam
|
||||||
|
side accordingly, and the witness is **one frontend across a
|
||||||
|
document→terminal replacement**, so only the target differs.
|
||||||
|
|
||||||
|
**Previously, revision 9 — SUPERSEDED.** Both items reversed a decision
|
||||||
|
revision 8 made:
|
||||||
|
|
||||||
- **Q#BP-R2 is OVERRULED — a terminal-chrome wheel is CONSUMED, not
|
- **Q#BP-R2 is OVERRULED — a terminal-chrome wheel is CONSUMED, not
|
||||||
clamped.** SGR wheel input is **coordinate-bearing**: `encode_mouse`
|
clamped.** SGR wheel input is **coordinate-bearing**: `encode_mouse`
|
||||||
|
|
@ -2133,17 +2154,53 @@ branch**. Two concrete wrongnesses follow: a reporting child gets a
|
||||||
`finish_selection`), and a chrome wheel becomes **local scrollback**
|
`finish_selection`), and a chrome wheel becomes **local scrollback**
|
||||||
instead of behaving as a content wheel does.
|
instead of behaving as a content wheel does.
|
||||||
|
|
||||||
| target | kind | producer | receiver |
|
##### The producer is TARGET-BLIND, and the table must respect that
|
||||||
|---|---|---|---|
|
|
||||||
| **document** | `Down(Left)`, `Down(Right)` | do not arm, do not send | reserved — drop |
|
**Revision 9 split the producer column by target. The GPU cannot make
|
||||||
| **document** | `Drag(Left)`, chrome-originated | not sent (never armed) | reserved — drop |
|
that split.** `PanelFrame` carries `buffer_id`, both epochs, `size`,
|
||||||
| **document** | `Drag(Left)`, **content-originated** | normalized to the last valid CONTENT cell, then **subject to the ordinary dedupe** | process when it arrives |
|
`cells`, `cursor` and `focused` — **and no target-kind discriminator**
|
||||||
| **document** | `Up(Left)` | **send** | **process** — terminates the gesture |
|
(`pmacs-protocol/src/panel.rs:73` onward). The panel is an **opaque
|
||||||
| **document** | wheel | **send** | **process** — scrolls the panel |
|
cell projection** to the frontend. `state.terminal`
|
||||||
| **terminal** | any kind, chrome-**originated** | do not arm, do not send | drop, matching the TUI |
|
(`pmacs-gpu/src/main.rs:1964`) describes the **primary full-window**
|
||||||
| **terminal** | `Drag`, content-originated, now over chrome | normalized, then **subject to the ordinary dedupe** | process when it arrives |
|
terminal, not the side-window, so it cannot answer the question either.
|
||||||
| **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 producer rule reading "terminal chrome wheel: do not send" is
|
||||||
|
therefore **unimplementable without a new wire field**, and this lane
|
||||||
|
is explicitly non-protocol-bearing. **Every producer rule must be
|
||||||
|
target-blind; every target-dependent decision belongs to the daemon**,
|
||||||
|
which resolves the side window and knows the buffer kind
|
||||||
|
(`is_terminal(buffer_id)`).
|
||||||
|
|
||||||
|
**Producer — by kind and origin only:**
|
||||||
|
|
||||||
|
| kind | origin | producer |
|
||||||
|
|---|---|---|
|
||||||
|
| `Down(Left)`, `Down(Right)` | chrome | **do not arm, do not send** |
|
||||||
|
| `Drag(Left)` | chrome | not sent — never armed |
|
||||||
|
| `Drag(Left)` | content, now over chrome | normalize to the last valid CONTENT cell, then **ordinary dedupe** |
|
||||||
|
| `Up(Left)` | content, now over chrome | **always send**, normalized |
|
||||||
|
| wheel | chrome | **claim it and send**, carrying its valid frame coordinate |
|
||||||
|
|
||||||
|
These collapse safely because the two targets want the **same producer
|
||||||
|
behaviour** everywhere except the wheel: a chrome press is reserved by
|
||||||
|
documents and dropped by terminals — both drop; a crossing `Up` must
|
||||||
|
arrive for both, to terminate a selection or to deliver a release. The
|
||||||
|
wheel is the sole divergence, and it is now decided receiver-side.
|
||||||
|
|
||||||
|
**Receiver — after validation and resolving the side window:**
|
||||||
|
|
||||||
|
| kind | document panel | terminal panel |
|
||||||
|
|---|---|---|
|
||||||
|
| chrome `Down`/`Drag` | reserved — drop | drop, matching the TUI |
|
||||||
|
| crossing `Drag` (normalized) | process when it arrives | process at the content coordinate |
|
||||||
|
| crossing `Up` (normalized) | terminate the gesture | **replay at the last valid CONTENT coordinate** — never the chrome row |
|
||||||
|
| chrome wheel | **process through `scroll_window`** | **CONSUME** — no child bytes, no local scrollback, no document fallthrough (Q#BP-R2) |
|
||||||
|
|
||||||
|
**The chrome wheel carries a chrome coordinate over the wire, and that
|
||||||
|
is fine**: it is a valid frame cell, the daemon's coord validation
|
||||||
|
accepts it, `scroll_window` is a window-level move that does not read
|
||||||
|
it, and the terminal branch never forwards it to a child. **The
|
||||||
|
coordinate is never fabricated and never reaches an application.**
|
||||||
|
|
||||||
**`Up` is the load-bearing crossing event, and the only one promised
|
**`Up` is the load-bearing crossing event, and the only one promised
|
||||||
unconditionally.** A content-originated gesture terminates at its last
|
unconditionally.** A content-originated gesture terminates at its last
|
||||||
|
|
@ -2181,6 +2238,31 @@ parity, which revision 8 traded away for the weaker argument.
|
||||||
|
|
||||||
Document chrome is unchanged: a wheel there still scrolls the panel.
|
Document chrome is unchanged: a wheel there still scrolls the panel.
|
||||||
|
|
||||||
|
**Enforced in the DAEMON, not the frontend.** Revision 9 wrote this as
|
||||||
|
a producer rule, which the GPU cannot implement — it has no way to know
|
||||||
|
the panel holds a terminal (see the seam note above). The producer
|
||||||
|
claims the chrome wheel and sends it for **every** panel; the daemon
|
||||||
|
resolves the side window and consumes it when the buffer is a terminal.
|
||||||
|
|
||||||
|
**Witness — one frontend across a document→terminal replacement**, so
|
||||||
|
the two outcomes are separated by nothing but the buffer kind:
|
||||||
|
|
||||||
|
1. panel shows a **document**; wheel over chrome → **the panel
|
||||||
|
scrolls**.
|
||||||
|
2. the panel is replaced by a **terminal**; wheel over chrome →
|
||||||
|
**nothing changes** — no child bytes, no scrollback movement, no
|
||||||
|
document scroll.
|
||||||
|
|
||||||
|
*Mutation: let the terminal branch call `apply_terminal_gesture` — the
|
||||||
|
chrome coordinate fails its reporting bounds check, falls into the
|
||||||
|
local branch, and the row catches the **accidental local scrollback**
|
||||||
|
that revision 8's clamp would have shipped deliberately.*
|
||||||
|
|
||||||
|
Doing it in one frontend across a replacement is what makes it a
|
||||||
|
control rather than two unrelated observations: the geometry, the
|
||||||
|
pointer position and the producer path are identical, and only the
|
||||||
|
target differs.
|
||||||
|
|
||||||
**Both crossings need witnesses, and they fail in opposite
|
**Both crossings need witnesses, and they fail in opposite
|
||||||
directions:**
|
directions:**
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue