Schema major 2 Phase F: ratify repeat + slur rendering into Chapter 7

The schema-major-2 push's rendering consumers (E1 repeat barlines/volta
brackets, E2 slur cubic-béziers) lifted into core spec Chapter 7 — layout-IR
only, non-canonical, so no wire form and no companion-version bump (Operation
Catalog stays 0.7.0, Binary Format 0.6.0), mirroring the schema-major-1 Phase-F
engrave-ratify-only precedent.

Three req:layoutir:* requirements added at §ResolvedLayoutIR:
- resolved-primitives: ResolvedLayoutIR carries non-glyph line Strokes (staff
  lines, stems, barlines, volta brackets — present since staff lines, never
  previously ratified) and cubic-bézier Curves (slurs, E2); each provenance-
  traced (the hit-test basis), re-spaced like a glyph, non-canonical. The
  struct listing gains strokes/curves and the Stroke/Curve shapes; the RenderIR
  provenance requirement widened from "originating ResolvedGlyph" to
  "ResolvedGlyph, Stroke, or Curve".
- repeat-render (E1): a barline-drawing RepeatStructure renders a repeat barline
  at each resolved boundary, each Volta a bracket with ending numbers;
  unresolvable boundaries draw no ink (honest placement); jump-kind marks and
  cross-region repeats deferred.
- slur-curve (E2): a Slur renders as a cubic bézier honoring CurvatureOverride;
  an endpoint not on a single staff of one region draws no curve; a non-Solid
  line style MUST be surfaced (diagnostic), not silently rendered solid.

The curvature-computing algorithm and the RenderIR encoding stay forward-
referenced out. Ch5 §Slurs / §Repeat Structures and the Ch7 Forward References
gain labels for the cross-references. Revision-history row + a schema-major-2
rendering tranche in PASS12_RATIFICATION_LOG.md; layout-ir DECISIONS carry the
Phase-F cross-refs. Spec builds clean (latexmk -xelatex, 0 undefined refs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-07-08 16:23:12 -04:00
parent 28f210e16f
commit 693150e43e
4 changed files with 167 additions and 5 deletions

View File

@ -483,6 +483,13 @@ emission set (successive-notehead no-collision chains + per-glyph containment
## Repeat barlines and volta brackets (schema major 2, E1, 2026-07-07)
> **Ratified (Phase F, 2026-07-08):** these decisions are now the normative
> Minimal-tier floor `req:layoutir:repeat-render` in core spec Chapter 7
> (§ResolvedLayoutIR); the non-glyph primitives the volta brackets use are
> `req:layoutir:resolved-primitives`. Layout geometry is non-canonical, so no
> wire/companion-version change (PASS12_RATIFICATION_LOG.md, schema-major-2
> tranche).
The first repeat-structure ink (Chapter 5 `RepeatStructure` / `RepeatKind` /
`Volta`, ratified by the major-2 Phase A). Rendering is spec-unconstrained
(Ch7's `BarLine` payload is undefined and voltas have no layout variant), so
@ -559,6 +566,15 @@ these are E1 implementation decisions for the Phase-F ratification pass:
## Slur curves + the cubic-bézier curve primitive (schema major 2, E2, 2026-07-08)
> **Ratified (Phase F, 2026-07-08):** the slur rendering floor is
> `req:layoutir:slur-curve` and the `Curve`/`Stroke` primitive vocabulary is
> `req:layoutir:resolved-primitives` in core spec Chapter 7 (§ResolvedLayoutIR)
> — the struct listing now carries `strokes`/`curves` and the `Stroke`/`Curve`
> shapes, and the RenderIR provenance requirement was widened to name all three
> primitive kinds. The curvature *algorithm* and dash rendering stay
> forward-referenced out. Non-canonical, so no wire/companion-version change
> (PASS12_RATIFICATION_LOG.md, schema-major-2 tranche).
The third pipeline primitive kind. Primitives were two parallel flat Vecs
(`glyphs`, `strokes`) at each of the three IR stages; a `Curve` (four control
points + thickness/layer/style/provenance, mirroring `Stroke`) adds a third

View File

@ -177,3 +177,34 @@ returned a successful empty derivation under the requested profile. Resolution:
the **spec text stands**; the code moved — `derive_annotations` now returns
`Result` and rejects unregistered ids up front (`PrePassError`). No spec
change; core DECISIONS records the reasoning.
## Schema-major-2 tranche (2026-07-08) — rendering (E1/E2), engrave ratify-only
The schema-major-2 push's *rendering* consumers, ratified into core spec
Chapter 7. The data model (Phases AC) and the repeat-authoring op pair (Phase
D) already landed with their own revision-history rows and companion-version
bumps (Operation Catalog 0.7.0, Binary Format 0.6.0); this tranche records
**Phase F**, which is layout-IR (Chapter 7) only. Delivered as the E1
(`7a9bf42` + `6651ae5`), E2 (`81b7f42` + `28f210e`) commit series. Layout
geometry is non-canonical (no content hash, no wire form — see
`req:layoutir:object-id-derivation`), so this is a ratify-as-implemented with
**no byte-layout change and no companion bump**, mirroring the schema-major-1
Phase-F precedent.
| Item | Disposition | Spec locus | Consumer |
|---|---|---|---|
| Non-glyph resolved primitives | **adopt (ratify pre-existing + E2 reality)** — Ch7 gains Requirement `req:layoutir:resolved-primitives`: `ResolvedLayoutIR` carries, beyond glyphs, non-glyph line `Stroke`s (staff lines, stems, barlines, volta brackets — present since staff lines, never previously ratified) and cubic-Bézier `Curve`s (slurs, E2). Each carries provenance (the hit-testing basis) and is re-spaced by the solver like a glyph; both are non-canonical. The struct listing and the `Stroke`/`Curve` shapes are added; the RenderIR provenance requirement widened from "originating `ResolvedGlyph`" to "…`ResolvedGlyph`, `Stroke`, or `Curve`" | core_spec Ch7 §ResolvedLayoutIR (`req:layoutir:resolved-primitives`) | `epiphany-layout-ir` (`constrained.rs::{Stroke,Curve}`, `resolved.rs`); `epiphany-render-svg` |
| E1 repeat / volta rendering | **adopt** — Ch7 gains Requirement `req:layoutir:repeat-render`: a barline-drawing `RepeatStructure` renders a repeat barline at each resolved boundary (the precomposed sign, replacing a coinciding measure barline or standing alone; the dot pair beside a never-replaced final barline), each `Volta` a bracket with ending numbers; an unresolvable boundary draws no ink (traced anchor, honest placement); jump-kind (`DaCapo`/`DalSegno`) marks and cross-region repeats deferred. The glyph vocabulary/spacing stay engraving-algorithm concerns | core_spec Ch7 §ResolvedLayoutIR (`req:layoutir:repeat-render`); references Ch5 §Repeat Structures | `epiphany-layout-ir` (`logical.rs`, `constrained.rs`); `epiphany-engrave` (casting) |
| E2 slur rendering | **adopt** — Ch7 gains Requirement `req:layoutir:slur-curve`: a `Slur` renders as a cubic Bézier between its endpoint columns, honoring `CurvatureOverride` (direction, height); an endpoint that does not resolve to a column on a single staff of one region draws no curve (traced anchor); a non-`Solid` `SpanStyle` line MAY be deferred but MUST be surfaced (a layout diagnostic), never silently rendered solid. The curvature algorithm and dash rendering stay forward-referenced out | core_spec Ch7 §ResolvedLayoutIR (`req:layoutir:slur-curve`); references Ch5 §Slurs and Phrase Marks | `epiphany-layout-ir` (`logical.rs`, `constrained.rs`, `hittest.rs`); `epiphany-editor-core` |
**Version movements.** None on the wire: core spec Chapter 7 gains three
`req:layoutir:*` requirements + the `Stroke`/`Curve` struct listings + a
revision-history row (Phase F). Operation Catalog stays 0.7.0, Binary Format
stays 0.6.0 — layout geometry is non-canonical.
**Deferred (documented, not open candidates).** Dashed/dotted slur *rendering*
(surfaced as a diagnostic today), kind-differentiated slur appearance
(Phrase/Editorial), jump-kind repeat marks, cross-region and cross-system-break
curve splitting (de Casteljau), and the `slur_shape` quality metric moving off
`0.0`-by-construction — all Standard-tier / Push-3 work, named in the crate
DECISIONS, none a Pass-13 ambiguity.

Binary file not shown.

View File

@ -5066,6 +5066,7 @@ pub struct CrossCuttingRegistry {
\end{lstlisting}
\subsection{Slurs and Phrase Marks}
\label{sec:graph:slurs}
\begin{lstlisting}[language=Rust]
pub struct Slur {
@ -5357,6 +5358,7 @@ pub enum MarkerKind {
\end{lstlisting}
\subsection{Repeat Structures}
\label{sec:graph:repeats}
\begin{lstlisting}[language=Rust]
pub struct RepeatStructure {
@ -9213,8 +9215,8 @@ pub enum BreakKind {
\label{sec:layoutir:resolved}
\texttt{ResolvedLayoutIR} is the output of the constraint solver:
every glyph has a definitive position. This is the IR consumed by
the renderer.
every glyph, stroke, and curve has a definitive position. This is the
IR consumed by the renderer.
\begin{lstlisting}[language=Rust]
pub struct ResolvedLayoutIR {
@ -9226,6 +9228,14 @@ pub struct ResolvedLayoutIR {
/// Glyph-level objects with resolved positions.
pub glyphs: Vec<ResolvedGlyph>,
/// Non-glyph line primitives (staff lines, stems, barlines,
/// volta brackets, ...), positioned alongside the glyphs.
pub strokes: Vec<Stroke>,
/// Cubic-Bezier curve primitives (slurs, ...), positioned
/// alongside the glyphs.
pub curves: Vec<Curve>,
/// Engraving decisions, including any made by the constraint
/// solver (e.g., soft system breaks placed by casting-off).
pub engraving_decisions: Vec<EngravingDecision>,
@ -9256,8 +9266,88 @@ pub struct ResolvedGlyph {
pub style: GlyphStyle,
pub layer: i32,
}
/// A line primitive the solver positions but the vertical-band
/// model does not contain (a staff line, stem, barline, or bracket).
pub struct Stroke {
pub provenance: Provenance,
pub from: Point2D,
pub to: Point2D,
pub thickness: StaffSpace,
pub layer: i32,
pub style: GlyphStyle,
}
/// A cubic-Bezier curve primitive (a slur), its four control
/// points in `p0`..`p3` drawing order.
pub struct Curve {
pub provenance: Provenance,
pub p0: Point2D,
pub p1: Point2D,
pub p2: Point2D,
pub p3: Point2D,
pub thickness: StaffSpace,
pub layer: i32,
pub style: GlyphStyle,
}
\end{lstlisting}
\begin{requirement}
\label{req:layoutir:resolved-primitives}
\textbf{Non-glyph resolved primitives (ratified schema-major-2,
Phase~F).} Beyond glyphs, \texttt{ResolvedLayoutIR} carries
non-glyph \emph{line-stroke} primitives (staff lines, stems,
barlines, brackets) and \emph{cubic-B\'ezier curve} primitives
(slurs). Each \MUST{} carry provenance --- the basis of hit-testing
(\ref{sec:layoutir:provenance}) --- and is positioned by the solver
like a glyph: a spanning stroke or curve is re-spaced by the same
horizontal coordinate map its endpoint columns move under. These
primitives are \emph{non-canonical}: like layout identifiers and
glyph positions, they are not part of document state and enter no
content hash (\ref{req:layoutir:object-id-derivation}). A conformant
renderer draws them alongside the glyphs; the renderer encoding
itself is out of scope (\ref{sec:layoutir:render}).
\end{requirement}
\begin{requirement}
\label{req:layoutir:repeat-render}
\textbf{Minimal-tier repeat and volta rendering (ratified
schema-major-2, Phase~F).} A \texttt{RepeatStructure}
(\ref{sec:graph:repeats}) whose kind draws barlines
(\texttt{SimpleRepeat}, \texttt{Volta}) \MUST{} render a repeat
barline at each of its resolved boundaries --- the precomposed
repeat sign, either replacing a coinciding measure barline or
standing at the boundary column when none coincides, and the
repeat-dot pair beside a region-closing final barline (which is
never replaced). Each \texttt{Volta} \MUST{} render a bracket
spanning its resolved extent, with its ending numbers. A boundary
that does not resolve to a spacing column draws \emph{no} ink: a
traced anchor preserves the structure's provenance rather than
placing a mark at a false position. The jump kinds
(\texttt{DaCapo}, \texttt{DalSegno}) draw no Minimal-tier marks ---
their segno / coda / instruction text awaits a text primitive ---
and a repeat spanning more than one region renders in no single
region at this tier. The precise glyph vocabulary and spacing are
engraving-algorithm concerns (\ref{sec:layoutir:forward}).
\end{requirement}
\begin{requirement}
\label{req:layoutir:slur-curve}
\textbf{Minimal-tier slur rendering (ratified schema-major-2,
Phase~F).} A \texttt{Slur} (\ref{sec:graph:slurs}) \MUST{} render
as a cubic-B\'ezier \texttt{Curve} arcing between its two endpoint
event columns, honoring an authored \texttt{CurvatureOverride}
(direction and apex height) when present and choosing them
otherwise. A slur whose endpoint does not resolve to a column on a
single staff of one region draws \emph{no} curve: a traced anchor
preserves its provenance rather than a floating arc. An authored
non-\texttt{Solid} \texttt{SpanStyle} line (dashed, dotted) \MAY{}
be deferred to a higher tier, but \MUST{} be surfaced (a layout
diagnostic), never silently rendered solid. The curvature-computing
algorithm and the dash rendering are engraving-specification
concerns (\ref{sec:layoutir:forward}).
\end{requirement}
\section{RenderIR (Interface Only)}
\label{sec:layoutir:render}
@ -9291,9 +9381,11 @@ pub struct RenderConfiguration {
\begin{requirement}
Implementations producing \texttt{RenderIR} \MUST{} preserve
provenance from \texttt{ResolvedLayoutIR}: every renderer primitive
\MUST{} be traceable to its originating \texttt{ResolvedGlyph} (and
therefore to its score graph source). This is the basis of
hit-testing, selection, and back-reference navigation in the UI.
\MUST{} be traceable to its originating \texttt{ResolvedLayoutIR}
primitive --- a \texttt{ResolvedGlyph}, \texttt{Stroke}, or
\texttt{Curve} --- and therefore to its score graph source. This is
the basis of hit-testing, selection, and back-reference navigation
in the UI.
The full RenderIR type and its production rules are specified in
the renderer specification, outside the scope of this document.
@ -9515,6 +9607,7 @@ pub struct GlyphCatalogIdentity {
\end{requirement}
\section{Forward References}
\label{sec:layoutir:forward}
\begin{itemize}
\item The constraint solver consuming \texttt{ConstrainedLayoutIR}
@ -15392,6 +15485,28 @@ layouts they own versus inherit:
per-payload stamping (create born at v2; delete a minor kind append
at major~0): Binary Format companion 0.5.0~$\rightarrow$~0.6.0.
\\
\today & Schema major 2 (rendering, Phase F) &
The rendering consumers of the schema-major-2 data model are
ratified into Chapter~7. \texttt{ResolvedLayoutIR} gains the
non-glyph primitive vocabulary it has always carried in
implementation --- line \texttt{Stroke}s (staff lines, stems,
barlines, volta brackets) and cubic-B\'ezier \texttt{Curve}s
(slurs) --- as \ref{req:layoutir:resolved-primitives}
(provenance-traced, non-canonical). Two Minimal-tier rendering
floors join the constraint floor: \ref{req:layoutir:repeat-render}
(E1: repeat barlines at resolved boundaries, volta brackets with
ending numbers, honest non-drawing of unresolvable boundaries,
jump-kind marks and cross-region repeats deferred) and
\ref{req:layoutir:slur-curve} (E2: a slur renders as a cubic
B\'ezier honoring \texttt{CurvatureOverride}, honest non-drawing of
unresolvable or cross-staff spans, a non-\texttt{Solid} line style
surfaced not silently rendered solid). The curvature-computing
algorithm and the \texttt{RenderIR} encoding stay forward-referenced
out (\ref{sec:layoutir:forward}, \ref{sec:layoutir:render}). Layout
geometry is non-canonical, so no wire form and no companion version
moves --- an engrave-side ratify-as-implemented, mirroring the
schema-major-1 Phase~F precedent.
\\
\bottomrule
\end{longtable}