Ratify primitive band ownership in Chapter 7; scope canonical_bytes' doc
Review follow-up. Both findings were about documentation lagging a contract the code now enforces, and the first is the one that matters: band ownership is the solver-facing correctness contract, so it belongs in the spec, not only in a crate's DECISIONS.md. Chapter 7's ratified Stroke/Curve listings gain `vertical_band`, and a new requirement (req:layoutir:primitive-band-ownership) states the rule as a MUST: every primitive declares its owning band, the projection MUST name a band that exists, and a vertical solver MUST take ownership from that declaration and MUST NOT infer it from geometry. The rationale is recorded because the requirement looks like a convenience until you see why it isn't: a stem shares its column with the staff above, and a slur's endpoints are lifted clear of its own staff into the zone where the nearest notehead belongs to the neighbour. A solver that guesses from proximity tears such primitives off their notes when it renegotiates the staff gaps -- which is exactly what happened, twice. The requirement also records that only glyphs are band *members*, and that the reference is non-canonical attribution metadata. Stroke's stale "the vertical-band model does not contain" gloss is dropped. canonical_bytes' doc claimed to encode "the full resolved layout". It encodes the rendering fingerprint and deliberately excludes attribution metadata. Stating that precisely required checking rather than paraphrasing: ResolvedGlyph drops its band on the way out of the constrained stage, while Stroke and Curve -- whose types are shared with that stage -- carry vertical_band through but never encode it. Two layouts differing only in band ownership draw identically and hash alike. Spec rebuilds clean: no LaTeX errors, no undefined references, new label registered. Gate green, no golden churn. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
efaebb9acd
commit
fc411ea7ac
|
|
@ -101,8 +101,18 @@ pub struct ResolvedLayoutIR {
|
|||
|
||||
impl ResolvedLayoutIR {
|
||||
/// The canonical serialized output (Appendix D §"Quantized Layout
|
||||
/// Coordinates"): the full resolved layout, with glyph positions quantized
|
||||
/// to the `1/1024` grid. Equivalent to [`CanonicalEncode::to_canonical_bytes`].
|
||||
/// Coordinates"): the layout's *rendering fingerprint*, with glyph positions
|
||||
/// quantized to the `1/1024` grid. Equivalent to
|
||||
/// [`CanonicalEncode::to_canonical_bytes`].
|
||||
///
|
||||
/// It encodes what a conformant renderer draws and what a conformance claim
|
||||
/// compares — every primitive's provenance, geometry, style, and layer — and
|
||||
/// **excludes non-canonical layout-attribution metadata**. Concretely:
|
||||
/// [`ResolvedGlyph`] drops its band on the way out of the constrained stage,
|
||||
/// while [`Stroke`] and [`Curve`] (whose types are shared with that stage)
|
||||
/// carry `vertical_band` through but do not encode it. Band ownership tells a
|
||||
/// vertical solver which staff owns a primitive; it draws nothing, so two
|
||||
/// layouts differing only in it are the same rendered layout and hash alike.
|
||||
///
|
||||
/// Two solves whose internal f32 computations agree to better than `1/2048`
|
||||
/// staff space at every coordinate produce identical bytes; two layouts that
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -9267,8 +9267,8 @@ pub struct ResolvedGlyph {
|
|||
pub layer: i32,
|
||||
}
|
||||
|
||||
/// A line primitive the solver positions but the vertical-band
|
||||
/// model does not contain (a staff line, stem, barline, or bracket).
|
||||
/// A line primitive the solver positions (a staff line, stem,
|
||||
/// barline, or bracket).
|
||||
pub struct Stroke {
|
||||
pub provenance: Provenance,
|
||||
pub from: Point2D,
|
||||
|
|
@ -9276,6 +9276,13 @@ pub struct Stroke {
|
|||
pub thickness: StaffSpace,
|
||||
pub layer: i32,
|
||||
pub style: GlyphStyle,
|
||||
|
||||
/// The vertical band this stroke belongs to: its owning staff,
|
||||
/// declared by the projection that emitted it. A vertical solver
|
||||
/// reads ownership from here rather than inferring it from
|
||||
/// geometry. Unlike a glyph, a stroke is not listed in
|
||||
/// `VerticalBand::members`; this is a one-way reference.
|
||||
pub vertical_band: VerticalBandId,
|
||||
}
|
||||
|
||||
/// A cubic-Bezier curve primitive (a slur), its four control
|
||||
|
|
@ -9293,6 +9300,11 @@ pub struct Curve {
|
|||
/// The line pattern the renderer strokes the path with
|
||||
/// (a slur's authored `SpanStyle.line`): solid, dashed, or dotted.
|
||||
pub line: LineStyle,
|
||||
|
||||
/// The vertical band this curve belongs to (see `Stroke`). A
|
||||
/// slur's endpoints are lifted clear of its own staff, so its
|
||||
/// owner is not recoverable from its geometry.
|
||||
pub vertical_band: VerticalBandId,
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
|
|
@ -9313,6 +9325,35 @@ pub struct Curve {
|
|||
itself is out of scope (\ref{sec:layoutir:render}).
|
||||
\end{requirement}
|
||||
|
||||
\begin{requirement}
|
||||
\label{req:layoutir:primitive-band-ownership}
|
||||
\textbf{Primitive band ownership is declared, not inferred.} Every
|
||||
primitive --- glyph, stroke, and curve alike --- \MUST{} declare the
|
||||
\texttt{VerticalBandId} of the band that owns it, and the projection
|
||||
\MUST{} name a band that exists in \texttt{vertical\_bands}. A
|
||||
vertical solver \MUST{} take a primitive's owning staff from that
|
||||
declaration and \MUSTNOT{} infer it from the primitive's geometry.
|
||||
Content owned by no staff (a page-margin annotation, a structure
|
||||
spanning several staves) names a band whose kind is not
|
||||
\texttt{Staff}.
|
||||
|
||||
This is a correctness requirement, not a convenience. A primitive's
|
||||
drawn position does not determine its owner: a stem shares its
|
||||
horizontal column with the staff above, and a slur's endpoints are
|
||||
\emph{lifted clear} of its own staff by construction, landing in the
|
||||
inter-staff zone where the nearest notehead routinely belongs to the
|
||||
neighbouring staff. A solver that guesses from proximity will
|
||||
attribute such a primitive to the wrong staff and, on renegotiating
|
||||
the staff gaps, tear it off the notes it belongs to.
|
||||
|
||||
Only glyphs are \emph{members} of a band
|
||||
(\texttt{VerticalBand::members}), which is what realizes the
|
||||
horizontal spring solve; a stroke's or curve's band reference is
|
||||
one-way. Like the primitives themselves, the reference is
|
||||
non-canonical layout-attribution metadata: it enters no content hash
|
||||
and no canonical encoding.
|
||||
\end{requirement}
|
||||
|
||||
\begin{requirement}
|
||||
\label{req:layoutir:repeat-render}
|
||||
\textbf{Minimal-tier repeat and volta rendering (ratified
|
||||
|
|
|
|||
Loading…
Reference in New Issue