Twinned from https://github.com/cfsh/ninefold/issues/388 by tools/beads/import_github.py's reconcile pass.
Suggestions from ninefold-reviewer's review of #386. Both are advisory residuals below the gate's severity floor — #386 is CONVERGED and nothing here should be folded back into it.
1. The fitted pitch window isn't probe-assertable. tools/probes/fit-notes.json can only assert expect_focus, because the roll's window header (NameOf(_baseMidi)–NameOf(_baseMidi + WindowSpan)) is drawn straight to the canvas and isn't surfaced through BarShown, so expect_bar can't reach it. #386 substituted unit tests on RollWindow, which is the right call for the math — but it means no probe can catch the *wiring* regressing (e.g. FitBaseFor silently not being called). Exposing the window range through a probe-visible property would let fit-notes.json assert the fit itself.
2. L3's glide is sized from a mixed old-rows/new-zoom CellH when the fit also changes zoom. ShiftBase computes _slideY += -moved * CellH *before* RecomputeLayout() (games/tonapse/ui/PianoRoll.cs:305-317), and CellH reads _pitches.Length (still the old row count) while _zoomOctaves has already been updated by FitBaseFor. Pre-#386 this never mattered — nothing changed zoom and base in the same call. Now L3 can, and moved can be ~36 semitones rather than R2's 12. Purely a ~120ms animation artifact; the settled layout is correct.
Dropped as not worth tracking: RollWindow.PadBelow/PadAbove are called only from tests. They read as the assertion vocabulary for #384's "2–3 empty rows" ask, which is a fair reason to keep them.
None.
No comments.