The last vertical-spring piece: the gaps BETWEEN a system's staves are
renegotiated so tightly ledgered or slurred adjacent staves — which the
constrained stage stacks at a fixed pitch — separate. ENGRAVER_VERSION 10 -> 11
(a multi-staff score whose staves press together shifts them apart; a
single-staff score, with no inter-staff pair, is byte-identical).
Attribution (vertical_band + owning-glyph, per the chosen approach): a glyph via
its vertical_band (VerticalBandKind::Staff -> StaffId); a stem/ledger via its
notehead (component_glyph); a staff line via its Staff source; a slur via the
notehead nearest its start. Spacing is horizontal-only, so a primitive's y is
unchanged from the source frame the attribution reads.
The solve: per system, per staff, collect the real content y-extent (glyphs,
strokes, curves — ledgers and slurs included); order staves top-to-bottom by
their staff-line reference y (order fixed); shift each staff down by the
cumulative amount needed to bring its gap to the one above up to the band
model's preferred inter-staff gap. staff_shift[(system, staff)] is a per-staff
dy the bake applies (Placement::sunk) atop the per-system dy, so glyphs,
strokes, curves, the staff/measure/system records, content bounds, hit-test, and
quality metrics all read the same shifted geometry. The shifts grow each
system's extent, which the vertical stacking and justification then consume.
Regression inter_staff_solve_separates_colliding_staves (the two-staff fixture's
staff-line gap opens past the fixed pitch; a single-staff score keeps one staff
per system) + the two_staff_close_content render golden updates (slice 1 tight,
slice 2 separated). Only that engrave golden churned; single-staff goldens
byte-stable. 947 tests, clippy 0, docs -D warnings, conformance 8/8.
This completes the Standard-tier layout story end to end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>