Rescope the band-ownership MUST to the constrained stage; ratify it
Review follow-up on fc411ea. Finding 1 was correct and was mine to own: I wrote
"every primitive -- glyph, stroke, and curve alike -- MUST declare a
VerticalBandId" and placed it in the ResolvedLayoutIR section, where the adjacent
ResolvedGlyph listing has no such field. Under a literal read the implementation
was non-conforming against a requirement I had just added.
The rule belongs to the stage that has the obligation. req:layoutir:primitive-
band-ownership moves to Ch7 §ConstrainedLayoutIR §Vertical Bands and is scoped to
"every primitive the projection presents to the solver". A new companion,
req:layoutir:resolved-band-ownership, says what survives: a resolved Stroke/Curve
MUST retain its band (casting-off and the inter-staff solve both relocate them
and must attribute them), while a ResolvedGlyph carries none -- its ownership is
consumed during the solve and already baked into its resolved position, so the
field would be dead weight in a rendering fingerprint. That requirement also
carries the non-canonical clause: canonical_bytes omits vertical_band even from
the primitives that do retain it.
Finding 2: the ConstrainedLayoutIR listing gains strokes/curves. They have been
in the code since staff lines and were never listed -- so an implementation
following Chapter 7 could not have supplied non-glyph ownership to the solver at
all, which makes this the listing gap that actually blocked the new MUST.
Finding 3: the ratification trail now records the tranche -- a revision-history
row and a PASS12 log section naming both requirements, the three listing
corrections, the two commits, and the fact that adopting it churned no golden
(the declared owner agrees with the inferred one across the corpus, which is what
licensed the swap).
The log also records a pre-existing listing gap I found while checking my own
citations: ConstrainedLayoutIR still elides break_origins and catalog. I had
first written that neither is named anywhere in core_spec, then verified and
found both types ARE specified (req:layoutir:break-origin-attribution; Ch7 §Glyph
Catalog Identity) -- only the struct listing is incomplete, and unlike
strokes/curves neither gap blocks an implementation. Left as a Pass-13 candidate
rather than widened into this tranche.
Spec rebuilds clean: no LaTeX errors, no undefined references, both new labels
registered. Workspace 30/30, conformance 8/8; no Rust changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fc411ea7ac
commit
ced4b7255e
|
|
@ -235,3 +235,46 @@ appearance (Phrase/Editorial's own line, distinct from the SpanStyle dash),
|
|||
duration-aware slur height (would pull the clamped short/long slurs back into
|
||||
the shallow-arc band), collision-aware slur reshaping (the Standard-tier
|
||||
`slur_shape` driver) — all Standard-tier work.
|
||||
|
||||
## Push-3 tranche (2026-07-09) — primitive band ownership
|
||||
|
||||
The Standard-tier inter-staff vertical solve needed to know which staff owns each
|
||||
primitive. `GlyphObject` had always declared its `vertical_band`; `Stroke` and
|
||||
`Curve` had not, so `epiphany-engrave` reconstructed ownership from geometry —
|
||||
and got it wrong twice, tearing stems (`4132a7a`) and then slurs (`b1bfe04`) off
|
||||
their own notes, both reaching a committed golden. A slur is the proof the
|
||||
inference cannot be made safe: its endpoints are lifted clear of its own staff by
|
||||
construction, into the zone where the nearest notehead belongs to the neighbouring
|
||||
staff. Ratified as implemented in `efaebb9` (code) and `fc411ea` (Ch7 listings).
|
||||
Layout-IR (Chapter 7) only, non-canonical — **no wire form, no companion version
|
||||
move**, and no golden churn (the declared owner agrees with the inferred one on
|
||||
the whole corpus, which is what licensed the swap).
|
||||
|
||||
| Item | Disposition | Spec locus | Consumer |
|
||||
|---|---|---|---|
|
||||
| Primitive band ownership | **adopt (new requirement)** — Ch7 gains `req:layoutir:primitive-band-ownership`: every primitive presented to the solver in `ConstrainedLayoutIR` (`GlyphObject`, `Stroke`, `Curve`) MUST declare the `VerticalBandId` that owns it and that band MUST exist; a vertical solver MUST take ownership from the declaration and MUST NOT infer it from geometry. Staff-less content names a non-`Staff` band. A band MUST therefore exist for every staff of a region and for its margin, members or not; only glyphs are band *members* (a stroke's/curve's reference is one-way) | core_spec Ch7 §ConstrainedLayoutIR §Vertical Bands (`req:layoutir:primitive-band-ownership`) | `epiphany-layout-ir` (`constrained.rs::{Stroke,Curve}.vertical_band`, band emission); `epiphany-engrave` (`casting.rs` attribution) |
|
||||
| Ownership through the resolved stage | **adopt (new requirement)** — Ch7 gains `req:layoutir:resolved-band-ownership`: a resolved `Stroke`/`Curve` MUST retain its `vertical_band` (casting-off and the inter-staff solve both relocate them and must attribute them); a `ResolvedGlyph` carries none, its ownership already baked into its resolved position. Band ownership is non-canonical attribution metadata — it enters no content hash and no canonical encoding, so `ResolvedLayoutIR::canonical_bytes` omits it even from the primitives that carry it | core_spec Ch7 §ResolvedLayoutIR (`req:layoutir:resolved-band-ownership`) | `epiphany-layout-ir` (`resolved.rs::canonical_bytes`) |
|
||||
| Struct listings | **adopt (ratify reality)** — the `ConstrainedLayoutIR` listing gains `strokes: Vec<Stroke>` / `curves: Vec<Curve>` (present in code since staff lines, never listed — without them Chapter 7 cannot describe supplying non-glyph ownership to the solver); the `Stroke`/`Curve` listings gain `vertical_band`; `Stroke`'s stale "the vertical-band model does not contain" gloss is dropped | core_spec Ch7 §ConstrainedLayoutIR, §ResolvedLayoutIR | `epiphany-layout-ir` |
|
||||
|
||||
**Version movements.** None on the wire: core spec Chapter 7 gains two
|
||||
`req:layoutir:*` requirements, three struct-listing corrections, and a
|
||||
revision-history row. Operation Catalog stays 0.7.0, Binary Format stays 0.6.0 —
|
||||
layout geometry is non-canonical.
|
||||
|
||||
**Known listing gap (pre-existing, not introduced here).** The `ConstrainedLayoutIR`
|
||||
listing still elides two fields the code carries: `break_origins: Vec<BreakOrigin>`
|
||||
and `catalog: GlyphCatalogIdentity`. Both *types* are specified — the former's
|
||||
semantics by `req:layoutir:break-origin-attribution`, the latter by Ch7 §Glyph
|
||||
Catalog Identity — so only the struct listing is incomplete, and neither gap
|
||||
blocks an implementation the way a missing `strokes`/`curves` would have. Tracked
|
||||
as a Pass-13 candidate, deliberately not fixed here: the listing correction in
|
||||
this tranche is scoped to exactly what `req:layoutir:primitive-band-ownership`
|
||||
depends on.
|
||||
|
||||
**Deferred (documented, not open candidates).** A height model for the
|
||||
inter-staff gap band — the missing piece behind both staff-less content placed
|
||||
*between* two staves (it holds still while the lower staff descends away) and
|
||||
`vertical_density_penalty` saturating at 1.0 when the solve targets content
|
||||
extents while the metric scores the realized gap against the band's *preferred*
|
||||
height. Both are named in `epiphany-engrave/DECISIONS.md`; neither is a Pass-13
|
||||
ambiguity.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -9138,9 +9138,48 @@ pub enum VerticalBandKind {
|
|||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{requirement}
|
||||
\label{req:layoutir:primitive-band-ownership}
|
||||
\textbf{Primitive band ownership is declared, not inferred.} Every
|
||||
primitive the projection presents to the solver in
|
||||
\texttt{ConstrainedLayoutIR} --- \texttt{GlyphObject},
|
||||
\texttt{Stroke}, and \texttt{Curve} alike --- \MUST{} declare the
|
||||
\texttt{VerticalBandId} of the band that owns it, and that band
|
||||
\MUST{} exist 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 gaps between a system's staves, tear it off the notes it belongs
|
||||
to.
|
||||
|
||||
A band \MUST{} therefore exist for every staff of a region, whether
|
||||
or not any glyph landed in it, and for the region's margin ---
|
||||
otherwise a primitive that names one has nowhere to point. A band
|
||||
MAY carry no members.
|
||||
|
||||
Only glyphs are \emph{members} of a band
|
||||
(\texttt{VerticalBand::members}), which is what realizes the spring
|
||||
solve; a stroke's or curve's band reference is one-way. What becomes
|
||||
of the declaration downstream is
|
||||
\ref{req:layoutir:resolved-band-ownership}.
|
||||
\end{requirement}
|
||||
|
||||
\subsection{Constraints}
|
||||
|
||||
The IR carries explicit constraint inputs for the solver:
|
||||
The IR carries explicit constraint inputs for the solver. Beyond
|
||||
glyphs it presents the non-glyph primitives --- \texttt{Stroke} and
|
||||
\texttt{Curve}, whose shapes are listed with
|
||||
\ref{req:layoutir:resolved-primitives} --- since the solver positions
|
||||
those too:
|
||||
|
||||
\begin{lstlisting}[language=Rust]
|
||||
pub struct ConstrainedLayoutIR {
|
||||
|
|
@ -9157,6 +9196,14 @@ pub struct ConstrainedLayoutIR {
|
|||
/// Glyph-level layout objects.
|
||||
pub glyphs: Vec<GlyphObject>,
|
||||
|
||||
/// Non-glyph line primitives (staff lines, stems, barlines,
|
||||
/// ledger lines, brackets) presented to the solver alongside
|
||||
/// the glyphs.
|
||||
pub strokes: Vec<Stroke>,
|
||||
|
||||
/// Cubic-Bezier curve primitives (slurs), likewise.
|
||||
pub curves: Vec<Curve>,
|
||||
|
||||
/// Additional constraints not captured by spring parameters.
|
||||
pub constraints: Vec<LayoutConstraint>,
|
||||
|
||||
|
|
@ -9326,32 +9373,26 @@ pub struct Curve {
|
|||
\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}.
|
||||
\label{req:layoutir:resolved-band-ownership}
|
||||
\textbf{Band ownership through the resolved stage.} A resolved
|
||||
\texttt{Stroke} and \texttt{Curve} \MUST{} retain the
|
||||
\texttt{vertical\_band} they were given in
|
||||
\texttt{ConstrainedLayoutIR}
|
||||
(\ref{req:layoutir:primitive-band-ownership}), so a solver that
|
||||
re-spaces or relocates them --- casting off into systems, or
|
||||
renegotiating the gaps between a system's staves --- can attribute
|
||||
each one to its owning staff. A \texttt{ResolvedGlyph} carries no
|
||||
band: a glyph's ownership is consumed during the solve and its
|
||||
outcome is already baked into the glyph's resolved position, so the
|
||||
field would be dead weight in a rendering fingerprint.
|
||||
|
||||
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.
|
||||
Band ownership is \emph{non-canonical layout-attribution metadata}.
|
||||
It enters no content hash and no canonical encoding
|
||||
(\ref{req:layoutir:object-id-derivation}): it draws nothing, so two
|
||||
layouts differing only in it are the same rendered layout. A
|
||||
conformant canonical encoding of \texttt{ResolvedLayoutIR} therefore
|
||||
omits \texttt{vertical\_band} even from the strokes and curves that
|
||||
carry it.
|
||||
\end{requirement}
|
||||
|
||||
\begin{requirement}
|
||||
|
|
@ -15570,6 +15611,28 @@ layouts they own versus inherit:
|
|||
unchanged, no version move). Layout geometry stays non-canonical --- no
|
||||
wire form, no companion version moves.
|
||||
\\
|
||||
\midrule
|
||||
\today & Layout IR (primitive band ownership, Push 3) &
|
||||
Chapter 7 gains \ref{req:layoutir:primitive-band-ownership}: every
|
||||
primitive the projection presents to the solver ---
|
||||
\texttt{GlyphObject}, \texttt{Stroke}, \texttt{Curve} --- declares the
|
||||
\texttt{VerticalBandId} that owns it, naming a band that exists, and a
|
||||
vertical solver takes ownership from that declaration rather than
|
||||
inferring it from geometry. Ratifies-as-implemented the contract a
|
||||
vertical solver needs and an inference cannot supply: a slur's
|
||||
endpoints are lifted clear of its own staff, so its owner is not
|
||||
recoverable from where it is drawn. The \texttt{ConstrainedLayoutIR}
|
||||
listing gains \texttt{strokes} / \texttt{curves} (present since staff
|
||||
lines, never listed) and the \texttt{Stroke} / \texttt{Curve} listings
|
||||
gain \texttt{vertical\_band}; a band now exists for every staff of a
|
||||
region and for its margin, members or not.
|
||||
\ref{req:layoutir:resolved-band-ownership} carries the declaration
|
||||
through the resolved stage on strokes and curves (a
|
||||
\texttt{ResolvedGlyph} keeps none --- its ownership is already baked
|
||||
into its position). Band ownership is non-canonical attribution
|
||||
metadata: it enters no content hash and no canonical encoding, so no
|
||||
wire form and no companion version moves.
|
||||
\\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue