test(panel): the terminal scroll-anchor row, which I had given up on

Closes the fourth terminal gap. The row I could not drive was
reproducible; **my delta had the wrong sign** --- `scroll_view(key,
viewport, 3)` moves, `-1` does not --- and I concluded the fixture was
at fault after three attempts rather than trying the other direction.

The sequence: `frame()` registers the panel terminal view, the key is
built from the side window, the viewport is `panel_grid_size` less its
mode-line row, forty published line feeds build the history, and the
baseline is taken AFTER that history exists. Taking it after is what
makes the leg discriminating --- a constant anchor leaves the
post-history baseline unchanged, so the row fails even while
`mapping_revision` is perfectly live.

Two mutations, each failing this row alone:

  constant ANCHOR, live revision   -> the anchor is not in the key
  constant REVISION, live anchor   -> the screen is not in the key

Neither passes on the other's evidence, which is the separation the
terminal half needed: `screen.rs` proves the counter classifies events,
the domain row proves the branch is taken, and these two prove the
daemon's key actually reads both halves of what
`view_mapping_identity` returns.

The owed-witness note is removed from the ledger.

Verified: focused suite 37/37, clippy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
Levi Neuwirth 2026-08-15 16:28:09 +02:00
parent 0bdfc9b6dd
commit 921989a77c
No known key found for this signature in database
2 changed files with 52 additions and 23 deletions

View File

@ -437,17 +437,6 @@ from #171 and #215.
three of them lost to these two signatures. U9's synthetic-load three of them lost to these two signatures. U9's synthetic-load
control remains unrun and is the cheapest thing that would either control remains unrun and is the cheapest thing that would either
implicate load or clear it. implicate load or clear it.
- **OWED WITNESS — terminal scroll-anchor movement at the daemon
level.** The anchor is in the key (`PanelMappingContent::Terminal`
carries it) and the review asked for a row proving the daemon's
generation moves when it does. **Three attempts failed to drive a
scroll from the panel fixture**: `scroll_lines` needs a viewport the
panel projection registers on its own schedule, and `scroll_view`
with an explicit content size still reports no movement after forty
line feeds, so the history it would move into is not accumulating as
the fixture assumes. **Recorded rather than faked.** Without it, a
`view_mapping_identity` returning a constant ANCHOR while reporting a
live revision passes every terminal row that exists.
- **Gates:** the four `bottom_panel_*` suites, the GUI 1a wire suite, - **Gates:** the four `bottom_panel_*` suites, the GUI 1a wire suite,
`PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`, and **`--protocol`**. `PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`, and **`--protocol`**.

View File

@ -1816,17 +1816,57 @@ fn g2_g3_a_terminal_panels_key_tracks_its_screen_and_anchor() {
advancing the revision until §5b's terminal correction" advancing the revision until §5b's terminal correction"
); );
// NOT YET WITNESSED: scroll-anchor movement at this level. // CHANGING: the view's **scroll anchor** moves, with the child's
// screen untouched. The same coordinate then names a different
// retained row.
// //
// The anchor IS in the key — `PanelMappingContent::Terminal` carries // This is the leg that separates the anchor from the screen
// it — but driving a scroll from here has defeated three attempts: // revision: the baseline is taken AFTER the history exists, so a
// `scroll_lines` needs a viewport the panel projection registers on // constant anchor leaves it unchanged and the assertion below fails
// its own schedule, and `scroll_view` with an explicit size still // even though `mapping_revision` is perfectly live.
// reports no movement after forty line feeds, so the history the let key = {
// scroll would move into is not accumulating the way this fixture let core = session.state.core.borrow();
// assumes. let side = core.side_window_for(FID).expect("side");
// pmacs::terminal::view::TerminalViewKey::new(FID, side, buffer_id)
// Recorded as OWED rather than faked. Without it, a };
// `view_mapping_identity` that returned a constant ANCHOR while let viewport = {
// reporting a live revision would pass every row above. let core = session.state.core.borrow();
let grid = core.panel_grid_size(FID).expect("a presentable panel");
pmacs_protocol::CellSize::new(grid.rows.saturating_sub(1), grid.cols)
};
for _ in 0..40 {
feed(&session, AnsiEvent::LineFeed);
}
let anchor_of = |session: &Session| {
session
.state
.terminal_manager
.borrow()
.view_mapping_identity(key)
.expect("the panel's terminal view")
.1
};
let before_scroll = mapping_generation(&mut session).expect("a key");
let anchor_before = anchor_of(&session);
assert!(
session
.state
.terminal_manager
.borrow_mut()
.scroll_view(key, viewport, 3),
"the scroll must actually move, or this leg proves nothing"
);
assert_ne!(
anchor_of(&session),
anchor_before,
"scrolling pins the view to a retained row, so the anchor moves"
);
let after_scroll = mapping_generation(&mut session).expect("a key");
assert!(
after_scroll > before_scroll,
"the anchor is part of the mapping — the same coordinate now \
names a different retained row, with the screen untouched"
);
} }