Answers review of revision 6. Still framing only; no implementation.
Three of the five are corrections to rules 6 introduced.
**R-c's "the last row is inert" WAS WRONG.** The TUI precedent I cited
is per KIND, not per row: `inner_rows` guards `Down(Left)` (:3303),
`Drag(Left)` (:3331) and `Down(Right)` (:3348), and deliberately does
NOT guard `Up(Left)` (:3339) or the wheel (:3358/:3362). A blanket
inert rule breaks two things at once --- a wheel over the mode line
stops scrolling, and a gesture begun in content and released over the
mode line NEVER TERMINATES.
And a receiver-only rule cannot work at all here, because the producer
arms first: `PanelCell` spans the whole frame, so a mode-line `Down`
runs `set_panel_pointer_held(true)` locally
(`pmacs-gpu/src/main.rs:2878`) before the daemon sees anything.
Dragging into content then emits an orphan `Drag` the daemon cannot
tell from a real one. R-c now carries a per-kind producer/receiver
table, both crossings as witnesses --- mode-line->content must emit no
Drag, content->mode-line must terminate --- and rules the wheel
explicitly.
**R-d COVERED PANEL IDENTITY AND MISSED GEOMETRY IDENTITY.** The two
epochs move independently by design: `panel_epoch` is stable across
ordinary frames while `geometry_epoch` moves on any new declaration
"including a font or scale change that leaves `CellSize` identical"
(`pmacs-protocol/src/panel.rs:61`). `next_geometry_declaration`
(`pmacs-gpu/src/main.rs:6847`) advances it and clears neither pointer
field, so a held gesture resumes UNDER A NEW GRID carrying epochs that
are current and valid --- acceptance 49 rejects stale epochs and this
one is not stale.
R-d also constrained only one of the two latch fields. Clearing
`pointer_held` alone kills the orphan drag but leaves
`last_pointer_cell`, and `panel_motion_is_new` (`:7238`) then
suppresses the successor's first same-cell `Move` as a duplicate. Four
mutations now, D1-D4, including the NEGATIVE one: an ordinary
same-identity refresh must not cancel a live gesture, or D1/D2 are
satisfiable by resetting on every frame and selection stops working.
**R-a WITNESSED ONLY THE TERMINAL.** `dispatch_pointer`'s `Down` arm
reads `extending = mods.contains(SHIFT)` and either keeps the anchor or
collapses the selection (`src/editor.rs:3673`), so Shift-click in a
document or listview panel is selection extension. Threading modifiers
into `apply_terminal_gesture` alone would have passed the proposed row
while leaving document panels broken --- a witness certifying half a
fix. Two rows now, A1 terminal and A2 document, with separate
mutations: a single "drop mods at the boundary" bites both and proves
the boundary matters, not that each CONSUMER is wired.
**R-b'S ROWS WERE SATISFIABLE BY DOING NOTHING.** "Only A's panel
changes" passes for an implementation that drops the tail entirely. B1
now pins A's anchor at the Down cell's byte and A's cursor at the Drag
cell's byte; B2 proves `Up` collapses an empty click. B4 and B5 carry
Q#BP16's existing multi-click and Context statements, which replay is
where they first become executable. B6 is a listview visit sentinel:
the row is selected and `on_visit` does NOT run, so Q#BP-R1's ruling is
falsifiable rather than merely asserted.
Gates: all nine green under `env -u TMPDIR`, log 20260814T120856Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 5. Still framing only; no implementation.
**Q#BP-R1 IS RULED: a single click SELECTS a listview row only.**
RET/SPC remain the activation path (`listview.lua:610`); no
click-to-visit, no double-click-to-visit. That is acceptance 48's own
wording, and it keeps document navigation from arriving as an
incidental consequence of wiring replay.
**REVISION 5 CONCLUDED THAT ACTIVATION ORDERING MADE REPLAY SAFE. IT
IS NECESSARY BUT NOT SUFFICIENT.** The ordering holds only for gestures
that activate, in a session nothing interleaves with, on a presentation
that does not change mid-gesture, with modifiers intact and the mode
line excluded. Each of those four provisos is an edge:
**R-a --- modifiers are dropped before the shared path.** `PanelPointer`
carries `mods`; the daemon destructures them into `..`
(`src/daemon.rs:2425`) and the dispatcher has no modifier parameter.
This breaks PRECEDENCE, not just fidelity: `apply_terminal_gesture`
gates child reporting on `!shift && ... && modes.mouse_sgr`
(`src/editor.rs:3534`), so Shift is the user's "select locally instead
of talking to the child" override. Zeroed, a Shift-drag over a
reporting terminal panel sends SGR. Row: Shift-drag selects locally and
the child receives NO bytes. Mutation: drop `mods` at the boundary.
**R-b --- `Drag` and `Up` do not activate**, and another frontend's
input can interleave between a Down and its Drag, so a replay reading
ambient active-window state acts on whatever is active then. The
framing now NAMES the mechanism: an explicit side-window cell->byte
adapter, and selection through a window-TARGETED path.
`activate_and_position` (`src/editor.rs:3795`) is both the precedent
and the trap --- its conversion is properly window-scoped, but it calls
`set_active_window_id`. Rows: panel A Down -> frontend B input -> A
Drag/Up changes only A; an orphan Drag/Up on a passive panel leaves the
document mirror byte-identical.
**R-c --- `panel_grid_size` is the FRAME, not the terminal viewport.**
The panel's last row is its mode line: projection derives content as
`rows - 1` (`src/editor.rs:2499`) while `panel_hit_test` reports across
the whole frame (`pmacs-gpu/src/main.rs:7184`), so a `PanelPointer` can
name the mode-line row. Passing the full grid to
`apply_terminal_gesture` would make the mode line a child terminal
cell. Terminal viewport is `rows - 1`; document replay follows the
TUI's existing rule, "Mode-line click: reserved"
(`src/editor.rs:3304`). Rows must distinguish content from chrome or an
off-by-one passes.
**R-d --- replacement leaves the frontend's gesture latch armed.**
`Absent` clears `pointer_held` and `last_pointer_cell`
(`pmacs-gpu/src/main.rs:6909`); `Present`->`Present` does not
(`:6913`). Press on A, A replaced by B, and the latch emits a Drag or
release for B with no B press --- and acceptance 49 CANNOT reject it,
because the event carries B's current epochs. 49 is a staleness gate
and this is not a stale event; it is a well-formed event from a gesture
whose presentation no longer exists. The fix has a precedent in the
same file: the DIVIDER drag latch already carries both epochs and
self-invalidates (`:7288`). The pointer latch never got it.
Three of the four are places where the tree already contains the right
answer and the panel path does not use it, which is worth saying
plainly: this is wiring that has to be done carefully, not mechanism
that has to be invented.
Gates: all nine green under `env -u TMPDIR`, log 20260814T115257Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Ground truth before code, and the lane entry in the same commit, per
the standing correction from #171 and #215 --- the one the 1b lane
missed. Base `githubsucks/main` @ `72da24a`. No implementation yet.
**AC48 IS NOT UNIMPLEMENTED. IT IS HALF IMPLEMENTED**, and nothing had
written the halves down separately, which is how "PanelPointer drives
listview rows, selection, terminal reporting and focus" could sit next
to a dispatcher that does none of the first three.
Landed already: click-to-focus and the terminal activation rule
(`src/editor.rs:2701`); the focused-only auto-scroll clamp with a
passive panel's `view_top` preserved (`:2569`, which has cited parent
48 all along); and the coalescing rules --- `Move`/`Drag` tails
coalesce while press/release/context/wheel stay lossless and ordered
(`pmacs-gpu/src/attach.rs:374`).
Missing: listview row selection, panel selection, terminal mouse
reporting, wheel replay.
**AND THE REPLAY IS MOSTLY WIRING.** `apply_terminal_gesture`
(`src/editor.rs:3525`) is already "the one terminal pointer path,
shared by both frontend kinds" and already drives child mouse
reporting, selection and scrollback; a panel terminal needs the SAME
CALL, with `side_window_for` + `TerminalViewKey` + the
`panel_grid_size` the dispatcher already fetches. `scroll_window`
(`:3845`) is window-scoped including its cursor carry, and documents
that a wheel "names the pane under the pointer and does NOT activate
it". This is why a wheel-only bridge would be the wrong shape: the
shared path takes every kind at once, and the wheel-only version would
have to grow a second one later.
**The scoping hazard, and why no ruling is needed.** `set_cursor_byte`,
`begin_selection` and `clear_selection` are ACTIVE-WINDOW scoped
(`src/editor_core.rs:1216`, `:4691`), so replaying naively from the
panel path would move the DOCUMENT's point --- exactly what AC48's
"without disturbing the document mirror" forbids. The existing
activation rule already resolves it: activation runs before replay in
the same dispatch, and the gestures that need the active-window API are
precisely the ones that activate. The single gesture that does not --- a
document panel's wheel --- needs only the window-scoped
`scroll_window`.
**One genuine question, opened not answered: Q#BP-R1.** Does a listview
row VISIT on click, or only select? `listview.lua` binds visiting to
RET/SPC (`:610`) and there is no pointer precedent anywhere in the
file. Single-click-visits matches a file tree; click-selects,
double-click-visits matches an editor list; AC48 says "row selection"
and nothing more. This lane implements SELECTION and does not invent
activation, because the difference is whether a click navigates the
user's document.
Gates: all nine green under `env -u TMPDIR`, log 20260814T113155Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Adds the approved framing for the bottom-panel arc: a buffer displayed
in a fixed-height window pinned to the bottom of the frame, targeted by
policy rather than by stealing the selected window, plus the missing
display-buffer/window-parameter concept underneath it.
Revision 4 follows three review rounds, an integration review, and a
landed-state audit against GPU initial target (#148, protocol v20) and
folding Stage 2 (#149).
Amended before branching by the pre-implementation dependency
verification recorded in section 0.6:
- the folding dependency is cleared and re-verified against canonical
main at ddaa80d (nothing in flight, folding Stage 2 acceptance 48/48
green, every borrowed anchor reproducing, and folding's only window.rs
edit confined to one 22-line hunk that leaves the layout functions
pre-folding code);
- R5-B1: Layout::compute has TWO production callers, not one. The
second, the peer-presence overlay pass in src/overlay_paint.rs, builds
its own text-area rect from active_layout() and never routes through
window_placements, so the planned compute(area, fixed) signature change
would otherwise leave every peer cursor painted at its no-panel row.
Corrected in section 1.1 and Q#BP2, pinned by acceptance 1, and the
fixed map is now specified to come from one shared helper rather than
being assembled per call site.
Stage 1 (window placement + TUI side windows) changes no wire shape.