Pin the two epoch races the session check alone cannot see

Q#BP16 step 3 compares an inbound event's geometry epoch against BOTH
the declaration the frontend was looking at and the daemon's latest
accepted one, and mutation testing showed the second half unpinned: no
fixture made the two diverge, so deleting it changed nothing. They
diverge exactly once — between a declaration being accepted and the
next frame answering it — which is the font/scale/resize race the epoch
exists for.

Also pins the attach/resize gate change at the seam it would break: a
semantic frontend's `Resize` must mint no frame geometry even when its
view is panel-capable, while a grid frontend's real frame size still IS
its declaration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T
This commit is contained in:
Levi Neuwirth 2026-07-28 17:32:27 -04:00
parent 817b134ae8
commit a251b89e2b
1 changed files with 159 additions and 0 deletions

View File

@ -6132,6 +6132,97 @@ mod tests {
);
}
/// Criterion 49's font/scale/resize race, and the reason Q#BP16 step
/// 3 compares the payload's geometry epoch against **both** the
/// shipped declaration and the daemon's latest accepted one.
///
/// A declaration accepted between two renders makes the two diverge:
/// the frontend is still looking at a frame answering the old epoch,
/// so a gesture matching that frame passes the session-side check
/// alone. Only the daemon-side comparison catches it — which is
/// exactly "an older retained frame neither paints nor accepts input
/// after a new `geometry_epoch` until a matching `Present` arrives".
#[test]
fn a_gesture_answering_a_superseded_geometry_is_dropped() {
let mut editor = crate::editor::EditorState::new();
let fid = FrontendId(712);
let (document, panel) = semantic_panel_view(&editor, fid, true);
let panel = panel.expect("panel window");
let mut semantic_states = HashMap::new();
semantic_states.insert(
fid,
crate::semantic_render::SemanticRenderState::for_peer(fid, PROTOCOL_VERSION),
);
editor.accept_semantic_frame_geometry(fid, 1, CellSize::new(24, 80));
let (geometry_epoch, panel_epoch) = shipped_declaration(&editor, fid, &mut semantic_states);
let buffer_id = editor.core.borrow().windows[&panel].buffer_id;
// Q#BP2S1: a font or scale change that leaves `CellSize`
// IDENTICAL still advances the epoch, and no frame answering it
// has been painted yet.
assert_eq!(
editor.accept_semantic_frame_geometry(fid, 2, CellSize::new(24, 80)),
crate::editor_core::GeometryUpdate::Advanced
);
assert_eq!(
semantic_states[&fid]
.panel_declaration()
.map(|frame| frame.geometry_epoch),
Some(geometry_epoch),
"fixture precondition: the SHIPPED declaration still answers the \
old epoch, so the session-side check alone would accept"
);
dispatch_panel_event(
&mut editor,
fid,
PROTOCOL_VERSION,
&mut semantic_states,
&mut HashMap::new(),
FrontendEvent::PanelPointer {
frontend_id: fid,
geometry_epoch,
panel_epoch,
buffer_id,
coord: pmacs_protocol::CellCoord::new(0, 0),
kind: pmacs_protocol::MouseKind::Down(pmacs_protocol::MouseButton::Left),
mods: pmacs_protocol::Modifiers::default(),
},
);
assert_eq!(
editor.core.borrow().views[&fid].active,
document,
"a gesture hit-tested against superseded geometry must drop"
);
// The accepted counterpart: once a frame answering the new epoch
// ships, the same gesture shape is honored again.
let (fresh_geometry, fresh_panel) = shipped_declaration(&editor, fid, &mut semantic_states);
assert_eq!(fresh_geometry, 2, "the new frame answers the new epoch");
dispatch_panel_event(
&mut editor,
fid,
PROTOCOL_VERSION,
&mut semantic_states,
&mut HashMap::new(),
FrontendEvent::PanelPointer {
frontend_id: fid,
geometry_epoch: fresh_geometry,
panel_epoch: fresh_panel,
buffer_id,
coord: pmacs_protocol::CellCoord::new(0, 0),
kind: pmacs_protocol::MouseKind::Down(pmacs_protocol::MouseButton::Left),
mods: pmacs_protocol::Modifiers::default(),
},
);
assert_eq!(
editor.core.borrow().views[&fid].active,
panel,
"…and a gesture answering the CURRENT geometry is accepted"
);
}
/// Criteria 49/50 for the resize event: matching epochs move the
/// stored request, a stale presentation epoch does not.
#[test]
@ -6188,6 +6279,74 @@ mod tests {
);
}
/// Parent acceptance 40, at the seam it would actually break: a
/// semantic frontend's `Resize` must not mint frame geometry, even
/// when its view is panel-capable.
///
/// Stage 1's gate was `panel_capable_for`, which was equivalent to
/// "grid" only because no semantic session could be panel-capable.
/// Once one can be, that gate feeds it exactly the placeholder
/// Q#BP15a forbids — and the failure would show up as a wrongly
/// sized first panel, far from this line.
#[test]
fn a_semantic_resize_does_not_mint_frame_geometry() {
let mut editor = crate::editor::EditorState::new();
let fid = FrontendId(710);
semantic_panel_view(&editor, fid, true);
let mut semantic_states = HashMap::new();
semantic_states.insert(
fid,
crate::semantic_render::SemanticRenderState::for_peer(fid, PROTOCOL_VERSION),
);
dispatch_panel_event(
&mut editor,
fid,
PROTOCOL_VERSION,
&mut semantic_states,
&mut HashMap::new(),
FrontendEvent::Resize {
frontend_id: fid,
size: CellSize::new(24, 80),
},
);
assert_eq!(
editor.core.borrow().frame_geometry_for(fid),
None,
"40: only FrontendCellGeometry is a semantic frontend's \
authoritative cell-equivalent declaration"
);
// The discriminating half: a GRID frontend's resize still is its
// declaration, so the gate is not simply switched off.
let grid = FrontendId(711);
let view = build_fresh_frontend_view(&mut editor, true, true);
editor.core.borrow_mut().register_frontend_view(grid, view);
let mut render_states = HashMap::new();
render_states.insert(grid, RenderState::new(CellSize::new(24, 80)));
dispatch_panel_event(
&mut editor,
grid,
PROTOCOL_VERSION,
&mut HashMap::new(),
&mut render_states,
FrontendEvent::Resize {
frontend_id: grid,
size: CellSize::new(30, 90),
},
);
assert_eq!(
editor
.core
.borrow()
.frame_geometry_for(grid)
.map(|geometry| geometry.total),
Some(CellSize::new(30, 90)),
"a grid frontend's real frame size IS its declaration"
);
}
/// Q#BP9's write-loop gate, independent of the producer's own flag.
#[test]
fn the_panel_frame_write_gate_rejects_v20_independently() {