From 557ea6dfacda48537efd59030650742edbe39673 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Thu, 20 Aug 2026 23:06:30 +0200 Subject: [PATCH] test(panel): P2 asserts real focus, and the ledger stops overstating Answers review of 9028e1b. P2 recorded `active_frontend`, which is which FRONTEND is current, not which window has focus. Focus is `views[&fid].active` and `focus_window` moves it, so the row would have watched a panel steal focus without noticing. It now records the focused window, and asserts up front that the panel does not already hold it --- otherwise "focus did not move to the panel" asserts nothing. P2's refusal lever also changed, because the old one could not exercise what the row claims. An out-of-grid cell with the row bound removed becomes `on_chrome`, so the press classifies Consumed and still reaches no target: the row bit on its own precondition while focus was never touched. A foreign buffer at an in-content cell is the refusal whose mis-gating actually yields Accepted. And the row now says what is falsifiable about it. Removing the buffer check makes the press Accepted and P2 fails --- but on the precondition, which fires first, so the focus, controller and byte assertions cannot fail under that mutation and no other mutation reaches them: the daemon applies only on Accepted, and the disposition enum gives Refused no target to apply. They are defence in depth against a future refactor, labelled as such rather than presented as coverage. The ledger claimed every row reads a target effect and never the latch alone. That was false. P9 and P10 read the LATCH, and correctly so --- the defect they fence is a record existing for a gesture that never began, so the record is the artifact, and an effect assertion would not distinguish their mutations. The line now separates effect rows from arming-gate rows and names P2's third case. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- docs/active-work.md | 19 ++++++++++++---- src/daemon.rs | 55 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/docs/active-work.md b/docs/active-work.md index f50e8b0..f8cfc9d 100644 --- a/docs/active-work.md +++ b/docs/active-work.md @@ -307,10 +307,21 @@ from #171 and #215 — the correction the 1b lane missed, honoured here. carrying a target — stayed representable inside `editor.rs`. Now `Refused` holds no target at all. - **Witnesses: G5k(a)–(d), P1, P2, P3 both legs, P4, P5, P7, P8, P9, - P10, P11, P12.** Each reads a TARGET EFFECT — the child's byte - stream (**exact bytes**, not a count), the terminal drag state, the - document selection, or focus and controller ownership — never the - latch alone. Each bites its own mutation, including G5k's verbatim. + P10, P11, P12**, each biting its own mutation, including G5k's + verbatim. **They do NOT all read a target effect, and an earlier + version of this line said they did.** Two kinds: + - **Effect rows** — G5k(a)–(d), P1, P3 both legs, P4, P5, P7, P8, + P11, P12 — read the child's byte stream (**exact bytes**, not a + count), the terminal drag state, or the document selection. + - **Arming-gate rows** — **P9 and P10** — read the LATCH, and that + is correct for them: the defect they fence is a record existing + for a gesture that never began, so the record IS the artifact. + Manufacturing an effect assertion for them would not distinguish + their mutations. + **P2 is a third case**: its falsifiable claim is the + classification; its focus, controller and byte assertions are + defence in depth, because the precondition asserting `Refused` + fires first under the only mutation that would reach them. - **Every fixture asserts its own precondition** (the disposition is `Accepted`, or is `Refused`) because four rows in these rounds passed vacuously: cells that were out of grid, or that clamped to diff --git a/src/daemon.rs b/src/daemon.rs index 60bc11e..20a843a 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -8520,6 +8520,16 @@ mod tests { /// P2, effect half — a REFUSED press reaches no target at all. /// + /// **What is falsifiable here is the CLASSIFICATION.** Removing the + /// buffer check makes this press `Accepted`, and the row fails. The + /// focus, controller and byte assertions cannot fail under that + /// same mutation, because the precondition asserting `Refused` + /// fires first — and no other mutation reaches them, since the + /// daemon calls `apply_panel_pointer` only on `Accepted` and the + /// disposition enum gives `Refused` no target to apply. They are + /// **defence in depth against a future refactor**, kept and + /// labelled rather than presented as witnessed coverage. + /// /// §5b's four `g5_substrate_a_refused_*` rows read the latch and the /// cancellation count; none of them reads the target. A refusal that /// armed nothing while still sending the child a press, or starting @@ -8531,20 +8541,41 @@ mod tests { terminal_panel_session(fid, true); let press = pmacs_protocol::MouseKind::Down(pmacs_protocol::MouseButton::Left); let none = pmacs_protocol::Modifiers::default(); - let outside = { - let core = editor.core.borrow(); - let grid = core.panel_grid_size(fid).expect("grid"); - pmacs_protocol::CellCoord::new(grid.rows, 0) - }; + // THE REFUSAL LEVER IS A WRONG BUFFER, at an IN-CONTENT cell. + // + // An out-of-grid cell cannot serve here: with the row bound + // removed it becomes `on_chrome`, so the press classifies + // `Consumed` and still reaches no target. The row would "bite" + // on its own precondition while never exercising focus at all. + // A foreign buffer at a content cell is the refusal whose + // mis-gating yields `Accepted`, which is the misclassification + // these assertions are written against. + let foreign_buffer = editor + .core + .borrow() + .registry + .borrow_mut() + .create("*not-the-panel*"); + let in_content = pmacs_protocol::CellCoord::new(1, 2); let key = crate::terminal::view::TerminalViewKey::new(fid, panel, buffer_id); - let active_before = editor.core.borrow().active_frontend; + // FOCUS is `views[&fid].active`, not `active_frontend`: the + // latter says which frontend is current, and `focus_window` + // moves the former. An earlier version of this row recorded the + // wrong one and would have watched a panel steal focus without + // noticing. + let focused_before = editor.core.borrow().views[&fid].active; + assert_ne!( + focused_before, panel, + "fixture: the panel must start PASSIVE, or 'focus did not \ + move to the panel' asserts nothing" + ); let controller_before = editor .terminal_manager .borrow() .controller_view_for_frontend(fid); assert_eq!( editor - .classify_panel_pointer(fid, buffer_id, outside, press) + .classify_panel_pointer(fid, foreign_buffer, in_content, press) .outcome(), crate::editor::PanelPointerOutcome::Refused, "fixture: this press really is refused --- asserted, because \ @@ -8557,8 +8588,8 @@ mod tests { &mut render, fid, epochs, - buffer_id, - outside, + foreign_buffer, + in_content, press, none, ); @@ -8569,9 +8600,9 @@ mod tests { receives is one it will expect a release for" ); assert_eq!( - editor.core.borrow().active_frontend, - active_before, - "and it must not ACTIVATE the panel: a misclassified press \ + editor.core.borrow().views[&fid].active, + focused_before, + "and it must not FOCUS the panel: a misclassified press focuses before its out-of-range anchor fails, so byte and \ latch assertions alone stay green while focus has moved" );