Schema major 2 Phase A: ratify the v2 data-model wire form (spec only)

Push 2's spec-first phase. core_spec (schema-major-2 revision): defines
the nineteen leaf types Ch5's ratified shapes referenced but never
pinned (SlurKind, CurveDirection, CurvatureOverride, the shared
SpanStyle record consolidating the identical SlurStyle/TieStyle/
SpannerStyle triplet, SubBeam, BeamGeometryOverride, SpannerKind's five
payload types + a leading Generic migration default, Volta,
MetadataValue, Timestamp, SoundConfiguration, TranspositionInterval
(structural, advisory until Ch4 per the P12-K2 discipline),
UnpitchedMember + member-resolution semantics (first-match, no-match
tolerated; StaffPosition vertical convention explicitly deferred),
SpaceUnit, LineStyle, StaffBracketKind); ratifies the visible-slice
clef/key content model into Ch5 (Clef/ClefShape/KeySignature/
ClefChange/KeySignatureChange) and retires the ClefId sketch;
strictly-authored metadata timestamps (req:graph:metadata-timestamps);
metadata `additional` = ordered list, duplicates permitted; Ch8 gains
the major-2 paragraph; Ch6 OperationKind listing gains its seven
missing variants (staleness fix).

binary_format 0.4.0 -> 0.5.0: new "Schema Major 2" section — per-
payload-type major assignment (snapshot-only Instrument/
RepeatStructure; SEVEN canonical embedding payloads incl. CreateRegion
transitively and SetStaffLayout's direct staff_lines_override field);
the MINIMAL-STAMPING rule (a block stamps the lowest major whose
layouts decode its bytes — deterministic, per-payload table given;
extends the major-1 no-restamp principle); append-after-prior-major
wire layouts with field-order divergences called out; new leaf layouts
with framing pinned (SubBeam EventIds = framed leaves; UnpitchedMember
fields = bare primitives; SpaceUnit = framed 12); total default-fill
v1->v2 migration table (complete StaffLineConfiguration defaults at
all four embed sites; RepeatKind default SimpleRepeat{count:2});
accept-set {0,1,2} with per-role staging note; CanonicalValue seam
list corrected 18 -> 22 (staleness fix).

Two review rounds (8 + 9 findings, all fixed pre-commit — the
stamping-rule determinism contradiction, the CreateRegion transitive
embedding, SubBeam leaf framing, and the SetStaffLayout direct-field
site being the load-bearing ones). Both PDFs build clean, zero
undefined references. No code changes; Phase B (snapshot side) next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
This commit is contained in:
Levi Neuwirth 2026-07-07 15:05:48 -04:00
parent 93f3dfde93
commit 7c9d1c40aa
4 changed files with 688 additions and 27 deletions

Binary file not shown.

View File

@ -235,7 +235,7 @@
{\Large\scshape\color{epiphanyslate}Binary Format}\\[6pt]
{\large\itshape\color{epiphanyslate}A companion to the Core Specification}\\[14pt]
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[24pt]
{\normalsize\color{epiphanyink}Version 0.4.0 --- Pass-12 G-ratification (appended effect-vocabulary discriminants; unsafe-edit tombstone encoding open question)}\\[4pt]
{\normalsize\color{epiphanyink}Version 0.5.0 --- Schema major 2 (data-model expansion: cross-cutting bodies, repeats/voltas, staff/instrument/metadata fills; total default-fill migration)}\\[4pt]
{\small\color{epiphanyslate}Normative for the byte layouts it defines}
\vfill
\end{titlepage}
@ -1132,7 +1132,7 @@ The public per-value codec --- the seam between this document and the
Operation Catalog's value-typed payloads (Operation Catalog,
\sectionsc{Value-Typed Payloads}, requirement
\texttt{req:catalog:value-encoding}) --- is defined for exactly these
eighteen types:
twenty-two types (the Phase-3 operation tranche added the last four):
\begin{quote}
\texttt{Event}, \texttt{Rest}, \texttt{Pitch}, \texttt{IdentifiedPitch},
@ -1140,7 +1140,9 @@ eighteen types:
\texttt{Spanner}, \texttt{RegionTimeModel}, \texttt{TimeAnchor},
\texttt{Region}, \texttt{StaffInstance}, \texttt{Voice},
\texttt{DecompositionAttachment}, \texttt{SpellingSourceKind},
\texttt{ScoreMetadata}, \texttt{MetricGrid}.
\texttt{ScoreMetadata}, \texttt{MetricGrid}, \texttt{Staff},
\texttt{TimeSignature}, \texttt{TempoSegment},
\texttt{StaffLineConfiguration}.
\end{quote}
The seam introduces \textbf{no new bytes}: a value's stand-alone canonical
@ -2237,7 +2239,9 @@ governed by the Binary Format companion specification, which defines the
wire encoding for each schema version''}). This chapter defines the wire
rules for schema evolution. Chapters~\ref{ch:values}--\ref{ch:barriers}
specify the schema-major-0 layouts; Section~\ref{sec:evolution:major1}
specifies the schema-major-1 delta and the migration between them.
specifies the schema-major-1 delta and the migration between them, and
Section~\ref{sec:evolution:major2} the schema-major-2 delta and its
migration from major~1.
\section{The Chunk-Level Gate}
\label{sec:evolution:gate}
@ -2251,9 +2255,11 @@ Every chunk declares a \texttt{SchemaVersion} (major, minor) in its
$[\textsc{min}, \textsc{max}]$ of majors and rejects any chunk whose major
falls \emph{outside} $[\textsc{min}, \textsc{max}]$ --- too new (above
\textsc{max}) or, once \textsc{min} rises past a retired major, too old
(Section~\ref{sec:evolution:major1}). Two majors are defined:
\tablenums{0} and \tablenums{1}; the reference implementation's accept-set
is $\{0, 1\}$ ($\textsc{min} = 0$, $\textsc{max} = 1$).
(Section~\ref{sec:evolution:major1}). Three majors are defined:
\tablenums{0}, \tablenums{1} (Section~\ref{sec:evolution:major1}), and
\tablenums{2} (Section~\ref{sec:evolution:major2}); the reference
implementation's accept-set is $\{0, 1, 2\}$ ($\textsc{min} = 0$,
$\textsc{max} = 2$).
\item \textbf{Minor} = additive. v0 readers verify the major only; the
minor is a \emph{record}, not a gate --- but it is a mandatory record:
a writer \MUST{} raise the chunk schema minor when it emits any
@ -2402,9 +2408,10 @@ discards the higher-major non-canonical caches.
\subsection{The accept-set gate}
A reader supports a contiguous set of majors $[\textsc{min}, \textsc{max}]$
(the reference implementation: $\{0, 1\}$, i.e. $\textsc{min} = 0$,
$\textsc{max} = 1$). The chunk-level gate
(Section~\ref{sec:evolution:gate}) rejects a chunk whose major falls outside
(at the major-1 revision the reference implementation's set was $\{0, 1\}$;
the \emph{current} set is stated once, in
Section~\ref{sec:evolution:gate}, and is not repeated here). The
chunk-level gate rejects a chunk whose major falls outside
$[\textsc{min}, \textsc{max}]$ --- too new above \textsc{max}, or too old
below \textsc{min} once a reader drops support for a retired major; it no
longer rejects on inequality with a single supported major.
@ -2499,6 +2506,295 @@ A reader migrates a major-0 acceleration \texttt{Score} snapshot on read
(cheaper than replaying the operation log); a writer emits only major-1 forms
for the changed payloads and leaves the canonical base at major~0.
\section{Schema Major 2}
\label{sec:evolution:major2}
Schema major~2 is the second data-model expansion major: it fills the
truncated bodies the v0 baseline deliberately staged to this companion ---
the cross-cutting structures (\texttt{Slur}, \texttt{Tie}, \texttt{Beam},
\texttt{Spanner}), \texttt{RepeatStructure}, \texttt{Staff} and
\texttt{StaffLineConfiguration}, \texttt{Instrument}, and
\texttt{ScoreMetadata} --- to the shapes the core specification's Chapter~5
ratifies (schema-major-2 revision). The leaf types those shapes reference
are defined in the same core revision; their wire layouts are below.
\subsection{Where the changed fields reach}
The major is assigned per payload type, exactly as in major~1:
\begin{itemize}
\item \textbf{Snapshot-only:} \texttt{Instrument} (no operation embeds
one) and \texttt{RepeatStructure} (no operation embeds one \emph{at
this revision}; the repeat-authoring operations planned for the same
push are born at v2 and change nothing about lower majors). These
reach only the acceleration full-\texttt{Score} snapshot.
\item \textbf{Canonical operation layer:} seven existing operation
payloads embed filled values ---
\texttt{CreateCrossCutting} and \texttt{ModifyCrossCutting} (a
\texttt{CrossCuttingValue} embedding \texttt{Slur}, \texttt{Tie},
\texttt{Beam}, or \texttt{Spanner}); \texttt{CreateStaff} (a
\texttt{Staff}, which also embeds the filled
\texttt{StaffLineConfiguration}); \texttt{CreateStaffInstance} (a
\texttt{StaffInstance}, whose \texttt{staff\_lines\_override} embeds
it); \texttt{SetStaffLayout} (whose \emph{own}
\texttt{staff\_lines\_override} field --- an op-payload field, not
part of a \texttt{StaffInstance} --- embeds it); \texttt{SetMetadata}
(a \texttt{ScoreMetadata}); and \texttt{CreateRegion}
\emph{transitively} --- its embedded \texttt{Region} may carry
\texttt{StaffInstance}s (\texttt{content.staff\_instances}), each
embedding the filled \texttt{StaffLineConfiguration}. (Reduction
preconditions a \emph{carried} region to be empty of typed children,
but the byte form is representable and preserved in the canonical
set, so the wire must define it.)
\textbf{Minimal stamping.} A writer \MUST{} stamp each
operation-envelope block with the \emph{lowest} schema major whose
layouts decode the block's bytes to the encoded values. The stamp
is therefore a pure function of the block's values --- two
conforming writers stamp identical content identically, which the
content hash requires (the schema version is in the chunk-hash
preimage). This is the same principle the major-1 section applies
to the canonical base: byte-identical content \MUSTNOT{} be
re-stamped at a higher major. A payload whose higher-major encoding
coincides byte-for-byte with its lower-major form takes the lower
major; a hidden embedding site (an \texttt{Option} that is
\texttt{None}, an empty \texttt{Vec}) contributes nothing.
Concretely, the per-payload minimal majors at this revision:
\texttt{CreateCrossCutting} / \texttt{ModifyCrossCutting} /
\texttt{CreateStaff} / \texttt{SetMetadata} $\Rightarrow$ always
\textbf{2} (their v2 fills are mandatory appended fields);
\texttt{CreateRegion} $\Rightarrow$ \textbf{2} iff a carried staff
instance's \texttt{staff\_lines\_override} is \texttt{Some}, else
\textbf{1} (the major-1 bool is always present);
\texttt{CreateStaffInstance} / \texttt{SetStaffLayout}
$\Rightarrow$ \textbf{2} iff their \texttt{staff\_lines\_override}
is \texttt{Some}, else \textbf{0}; every other payload keeps its
prior major. A block stamps the maximum over its payloads.
(Operation names are the Operation Catalog's; core Chapter~6's
enum spells the creates \texttt{InsertStaff} /
\texttt{InsertStaffInstance} / \texttt{InsertRegion}.)
\item \textbf{The canonical base is unchanged.} The
\texttt{MaterializedState} embeds none of the filled types (its
values are effects, conflicts, anomalies, object states, pitch
spellings, break keys, and pending records), so it remains major~0,
byte-identical across the bump --- the same keystone as major~1, and
a conformance test \SHOULD{} assert it again.
\item The manifest stays major~0 (nothing here touches it).
\end{itemize}
\subsection{Cross-major reader behaviour}
The major-1 rules extend unchanged: non-canonical chunks at a foreign
major are discarded and regenerated (or migrated on read, for the
acceleration snapshot); canonical chunks parse or the bundle opens
read-only. Migration \emph{composes}: a major-2 reader migrates a major-0
snapshot v0${\to}$v1${\to}$v2 in one read, each step total and
default-filling. A major-1-only reader opens a bundle read-only iff it
carries an op block bearing a v2 value; a major-0-only reader
additionally on v1 \texttt{CreateRegion} blocks, as before.
\emph{Staging note.} As with major~1, the reference implementation
raises chunk admission \emph{per role} as the implementation phases of
this revision's push land (snapshot decode, then the canonical
operation layer); the accept-set stated in
Section~\ref{sec:evolution:gate} is the ratified end-state, not a claim
that every intermediate commit already admits it.
\subsection{Changed value layouts}
As in major~1, \textbf{the wire form ratifies the code, not the model's
presentational field order}: a struct's v2 layout appends its new fields
\emph{after its existing major-0/major-1 fields}, even where Chapter~5's
listing interleaves them. Concretely (every leaf under
Chapter~\ref{ch:values} regime~(a) rules: LE integers, one discriminant
byte per union, \texttt{u32} counts and length prefixes, one presence
byte per \texttt{Option}, one bare byte per bool):
\begin{itemize}
\item \texttt{Slur} $=$ \texttt{id} \cat{} \texttt{start\_event} \cat{}
\texttt{end\_event} \cat{} \textbf{\texttt{kind}} \cat{}
\textbf{\texttt{curvature\_override}} \cat{} \textbf{\texttt{style}}.
\item \texttt{Tie} $=$ (major-0 fields) \cat{} \textbf{\texttt{style}}.
\item \texttt{Beam} $=$ \texttt{id} \cat{} \texttt{events} \cat{}
\texttt{level} \cat{} \textbf{\texttt{sub\_beams}} \cat{}
\textbf{\texttt{geometry\_override}}.
\item \texttt{Spanner} $=$ \texttt{id} \cat{} \texttt{start} \cat{}
\texttt{end} \cat{} \texttt{staves} \cat{} \textbf{\texttt{kind}}
\cat{} \textbf{\texttt{style}} (the kind appends \emph{after}
\texttt{staves}; Chapter~5 lists it second, presentationally).
\item \texttt{RepeatStructure} $=$ \texttt{id} \cat{} \texttt{start}
\cat{} \texttt{end} \cat{} \textbf{\texttt{kind}} \cat{}
\textbf{\texttt{voltas}}.
\item \texttt{Staff} $=$ \texttt{id} \cat{} \texttt{name} \cat{}
\texttt{abbreviation} \cat{} \texttt{instrument} \cat{}
\texttt{default\_staff\_lines} \cat{} \texttt{group} \cat{}
\textbf{\texttt{default\_clef}} (appended last; Chapter~5 lists it
mid-struct).
\item \texttt{StaffLineConfiguration} $=$ \texttt{line\_count} \cat{}
\textbf{\texttt{line\_spacing}} \cat{} \textbf{\texttt{line\_style}}
\cat{} \textbf{\texttt{bracket}}. This value is embedded by
\texttt{Staff} \emph{and} by
\texttt{StaffInstance.staff\_lines\_override}, which is how
\texttt{CreateStaffInstance}, \texttt{SetStaffLayout}, and
(transitively, through a carried region's staff instances)
\texttt{CreateRegion} become v2 payloads without gaining fields of
their own (the \texttt{CreateRegion}-in-major-1 pattern).
\item \texttt{Instrument} $=$ \texttt{id} \cat{} \texttt{name} \cat{}
\texttt{range} \cat{} \textbf{\texttt{abbreviation}} \cat{}
\textbf{\texttt{sound\_config}} \cat{}
\textbf{\texttt{transposition}} \cat{}
\textbf{\texttt{default\_clef}} \cat{}
\textbf{\texttt{default\_staff\_lines}} \cat{}
\textbf{\texttt{unpitched\_members}} (appended after the major-1
order \texttt{id}/\texttt{name}/\texttt{range}; Chapter~5's listing
order is presentational).
\item \texttt{ScoreMetadata} $=$ \texttt{title} \cat{}
\texttt{composer} \cat{} \texttt{copyright} \cat{}
\textbf{\texttt{subtitle}} \cat{} \textbf{\texttt{lyricist}} \cat{}
\textbf{\texttt{arranger}} \cat{}
\textbf{\texttt{creation\_timestamp}} \cat{}
\textbf{\texttt{modification\_timestamp}} \cat{}
\textbf{\texttt{additional}} (appended after the major-0 order).
\end{itemize}
\subsection{New leaf-type layouts}
\begin{itemize}
\item \texttt{SlurKind}: one byte --- \tablenums{0} Legato,
\tablenums{1} Phrase, \tablenums{2} Articulation,
\tablenums{3} Editorial.
\item \texttt{CurveDirection}: one byte --- \tablenums{0} Above,
\tablenums{1} Below.
\item \texttt{CurvatureOverride} $=$ \texttt{direction}
(\texttt{Option<CurveDirection>}) \cat{} \texttt{height}
(\texttt{Option<SpaceUnit>}).
\item \texttt{LineStyle}: one byte --- \tablenums{0} Solid,
\tablenums{1} Dashed, \tablenums{2} Dotted.
\item \texttt{SpanStyle} (one shared record: \texttt{Slur.style},
\texttt{Tie.style}, \texttt{Spanner.style}) $=$ \texttt{line}
(\texttt{LineStyle}) \cat{} \texttt{thickness}
(\texttt{Option<SpaceUnit>}).
\item \texttt{SubBeam} $=$ \texttt{level} (one byte) \cat{}
\texttt{events} (\texttt{u32} count \cat{} \texttt{EventId}
\emph{leaves}, each \texttt{u32}-length-framed 16 canonical bytes
$= 20$ bytes --- this chapter's leaf framing, exactly as
\texttt{Beam.events} encodes today).
\item \texttt{BeamGeometryOverride} $=$ \texttt{slope}
(\texttt{Option<CanonicalF64>}; dimensionless) \cat{}
\texttt{offset} (\texttt{Option<SpaceUnit>}).
\item \texttt{SpannerKind}: discriminant byte \cat{} payload ---
\tablenums{0} Generic; \tablenums{1} Hairpin \cat{}
\texttt{HairpinDirection} (one byte: \tablenums{0} Crescendo,
\tablenums{1} Diminuendo); \tablenums{2} OctaveLine \cat{}
\texttt{OctaveOffset} (one \texttt{i8}); \tablenums{3} PedalLine
\cat{} \texttt{PedalKind} (one byte: \tablenums{0} Sustain,
\tablenums{1} Sostenuto, \tablenums{2} UnaCorda);
\tablenums{4} TrillExtension; \tablenums{5} Glissando;
\tablenums{6} Portamento; \tablenums{7} TextLine \cat{}
\texttt{TextLineDefinition} ($=$ one length-prefixed raw-UTF-8
string); \tablenums{8} Bracket \cat{} \texttt{BracketKind} (one
byte: \tablenums{0} Square).
\item \texttt{RepeatKind}: discriminant byte \cat{} payload ---
\tablenums{0} SimpleRepeat \cat{} \texttt{count} (\texttt{u32} LE);
\tablenums{1} DaCapo \cat{} \texttt{end\_target}
(\texttt{TimeAnchor}); \tablenums{2} DalSegno \cat{} \texttt{segno}
\cat{} \texttt{end\_target}; \tablenums{3} Volta.
\item \texttt{Volta} $=$ \texttt{endings} (\texttt{u32} count \cat{}
bare \texttt{u32} LE values --- fixed-width primitives are
unframed) \cat{} \texttt{start} \cat{} \texttt{end}
(\texttt{TimeAnchor}s).
\item \texttt{SpaceUnit} $=$ one \texttt{CanonicalF64}, leaf-framed
($12$ bytes) --- byte-identical to how \texttt{CanonicalF64} itself
encodes; the newtype adds no bytes.
\item \texttt{StaffBracketKind}: one byte --- \tablenums{0} Brace,
\tablenums{1} Bracket.
\item \texttt{SoundConfiguration} $=$ \texttt{u32} length \cat{} raw
bytes (opaque; never interpreted by the core).
\item \texttt{TranspositionInterval} $=$ \texttt{diatonic\_steps}
(\texttt{i32} LE) \cat{} \texttt{chromatic\_steps} (\texttt{i32} LE).
\item \texttt{UnpitchedMember} $=$ \texttt{member} (bare \texttt{u32}
LE --- \texttt{UnpitchedMemberId} is an instrument-scoped small
value, \emph{not} a 128-bit object identifier, so the
identifier-leaf framing does not apply; this matches how
\texttt{UnpitchedEvent.instrument\_member} has encoded since
major~0) \cat{} \texttt{name} (string) \cat{}
\texttt{staff\_position} (bare \texttt{i16} LE, likewise).
\item \texttt{Timestamp} $=$ one \texttt{i64} LE (nanoseconds since
the Unix epoch, UTC).
\item \texttt{MetadataEntry} $=$ \texttt{key} (string) \cat{}
\texttt{value}; \texttt{MetadataValue}: discriminant byte \cat{}
payload --- \tablenums{0} Text \cat{} string; \tablenums{1} Integer
\cat{} \texttt{i64} LE; \tablenums{2} Flag \cat{} bool.
\item \texttt{Clef}, \texttt{ClefShape}, \texttt{KeySignature},
\texttt{ClefChange}, \texttt{KeySignatureChange} are \emph{not new
wire forms}: they have carried major-0 layouts since the
visible-slice revision (empty sequences left \texttt{Score} bytes
unchanged). Major~2 only \emph{embeds} \texttt{Clef} in two more
places (\texttt{Staff.default\_clef},
\texttt{Instrument.default\_clef}). Its layout, for the record:
\texttt{shape} (one byte: \tablenums{0} G, \tablenums{1} F,
\tablenums{2} C, \tablenums{3} Percussion) \cat{} \texttt{line}
(\texttt{i8}) \cat{} \texttt{octave\_shift} (\texttt{i8});
\texttt{KeySignature} $=$ \texttt{fifths} (\texttt{i8}; decoders
\MUST{} reject $|\texttt{fifths}| > 7$).
\end{itemize}
As in major~1: every embedded \emph{leaf} keeps this chapter's framing
(an identifier is a \texttt{u32} LE length \cat{} its 16 canonical
bytes $= 20$; a \texttt{CanonicalF64} is a \texttt{u32} LE length
\cat{} 8 bytes $= 12$); fixed-width primitive integers and bools inside
structs are bare; every \texttt{Option} is one presence byte then the
payload when present.
\subsection{Migration from major 1}
Total and default-filling, needing no score context; composes after the
major-1 migration for major-0 input. The defaults reproduce today's
rendering exactly: a migrated document draws byte-identically.
\begin{center}
\begin{tabular}{p{1.9in} p{3.4in}}
\toprule
\textbf{major-1 form} & \textbf{major-2 form} \\
\midrule
\texttt{Slur} & append \texttt{kind} $=$ Legato,
\texttt{curvature\_override} $=$ \texttt{None}, \texttt{style} $=$
(Solid, \texttt{None}) \\
\texttt{Tie} & append \texttt{style} $=$ (Solid, \texttt{None}) \\
\texttt{Beam} & append \texttt{sub\_beams} $=$ empty,
\texttt{geometry\_override} $=$ \texttt{None} \\
\texttt{Spanner} & append \texttt{kind} $=$ Generic, \texttt{style}
$=$ (Solid, \texttt{None}) \\
\texttt{RepeatStructure} & append \texttt{kind} $=$ SimpleRepeat
\{count $=$ 2\} (a v1 repeat \emph{meant} a repeat), \texttt{voltas}
$=$ empty \\
\texttt{Staff} & append \texttt{default\_clef} $=$ treble
(G, line 2, shift 0) \\
\texttt{StaffLineConfiguration} & append \texttt{line\_spacing} $=$
1.0, \texttt{line\_style} $=$ Solid, \texttt{bracket} $=$
\texttt{None} --- at \emph{every} embedding site:
\texttt{Staff.default\_staff\_lines},
\texttt{Instrument.default\_staff\_lines} (v2-new),
\texttt{StaffInstance.staff\_lines\_override}, and the
\texttt{SetStaffLayout} op payload's own
\texttt{staff\_lines\_override} field \\
\texttt{Instrument} & append \texttt{abbreviation} $=$ \texttt{None},
\texttt{sound\_config} $=$ empty, \texttt{transposition} $=$
\texttt{None}, \texttt{default\_clef} $=$ treble,
\texttt{default\_staff\_lines} $=$ the complete v2 default
(\texttt{line\_count} $=$ 5, \texttt{line\_spacing} $=$ 1.0,
\texttt{line\_style} $=$ Solid, \texttt{bracket} $=$ \texttt{None}),
\texttt{unpitched\_members} $=$ empty \\
\texttt{ScoreMetadata} & append \texttt{subtitle} / \texttt{lyricist}
/ \texttt{arranger} $=$ \texttt{None}, both timestamps $=$
\tablenums{0} (the unset convention), \texttt{additional} $=$
empty \\
the seven embedding op payloads (incl.\ \texttt{CreateRegion}'s
transitively embedded staff instances) & default-fill the embedded
value(s) as above; \emph{canonical} --- a v0/v1 op block migrates on
read, and a block bearing any v2-dependent bytes is major~2 (a
lower-major-only reader opens the bundle read-only) \\
canonical-base \texttt{MaterializedState} & unchanged, byte-identical,
stays major~0 \\
\bottomrule
\end{tabular}
\end{center}
% ===========================================================================
\chapter{Non-Canonical Pinned Encodings}
\label{ch:noncanon}
@ -2803,6 +3099,29 @@ layouts of Section~\ref{sec:values:representative}.
no existing assignment changed. Adds the unsafe-edit tombstone
\emph{encoding} open question (from P12-E5; semantics ratified in core
Chapter~8). Semantics: Operation Catalog 0.6.0. \\
\today & Schema evolution / Graph value layouts & 0.5.0 --- Defines
\textbf{schema major~2}, the second data-model expansion major
(Section~\ref{sec:evolution:major2}): fills the staged truncations ---
\texttt{Slur} (kind / curvature override / style), \texttt{Tie} (style),
\texttt{Beam} (sub-beams / geometry override), \texttt{Spanner} (kind,
with the leading \texttt{Generic} migration default / style),
\texttt{RepeatStructure} (kind / voltas), \texttt{Staff}
(\texttt{default\_clef}), \texttt{StaffLineConfiguration} (spacing /
line style / bracket), \texttt{Instrument} (six appended fields), and
\texttt{ScoreMetadata} (six appended fields incl.\ the
strictly-authored timestamps) --- with layouts for the new leaf types
and the total default-filling v1${\to}$v2 migration table. Assigns the
major per payload type: snapshot-only for \texttt{Instrument} /
\texttt{RepeatStructure}; \emph{canonical} for the seven embedding op
payloads (\texttt{Create}/\texttt{ModifyCrossCutting},
\texttt{CreateStaff}, \texttt{CreateStaffInstance},
\texttt{SetStaffLayout}, \texttt{SetMetadata}, and
\texttt{CreateRegion} transitively through carried staff instances);
the canonical base and manifest stay major~0, byte-identical.
Accept-set widens to $\{0, 1, 2\}$ (staged per role as the
implementation phases land). Semantics: core specification schema-major-2 revision
(leaf-type definitions, clef/key content-model ratification,
\texttt{ClefId} retirement, strictly-authored timestamp requirement). \\
\bottomrule
\end{longtable}

Binary file not shown.

View File

@ -2606,8 +2606,11 @@ only the static time structure of the score.
trajectory) is defined in Chapter~\ref{ch:graph}.
\item Re-anchoring rules for time anchors when their targets are
deleted are defined per-operation in Chapter~\ref{ch:semops}.
\item \texttt{SpaceUnit}, \texttt{TupletDisplay}, and other purely
display-oriented types are defined in Chapter~\ref{ch:layout-ir}.
\item \texttt{SpaceUnit} is defined normatively in
Chapter~\ref{ch:graph} as of schema major~2 (it entered canonical
state through \texttt{StaffLineConfiguration}).
\texttt{TupletDisplay} and other purely display-oriented types are
defined in Chapter~\ref{ch:layout-ir}.
\end{itemize}
% ===========================================================================
@ -3531,11 +3534,47 @@ pub struct MetadataEntry {
pub key: String,
pub value: MetadataValue,
}
/// The value of an additional metadata entry (defined schema major 2).
/// Closed small union; growth is by appended variant.
pub enum MetadataValue {
Text(String),
Integer(i64),
Flag(bool),
}
/// A calendar timestamp (defined schema major 2): nanoseconds since the
/// Unix epoch, UTC, no zone. Distinct from WallClockTime, which is
/// *performance* time within a score. The zero value is the "unset"
/// convention (genesis without a declared creation time).
pub struct Timestamp(pub i64);
\end{lstlisting}
The metadata structure is deliberately small. Extended bibliographic,
catalog, and editorial-apparatus metadata is the province of foreign-
format mappings and analytical layers, not the core.
\texttt{additional} is an \emph{ordered authored list}, not a map
(defined schema major 2): entry order is preserved verbatim in
canonical bytes, and duplicate keys are permitted --- foreign formats
legitimately carry repeated keys (multiple contributors, multiple
identifiers of one scheme). Consumers wanting map semantics take the
first entry per key.
\begin{requirement}
\label{req:graph:metadata-timestamps}
\textbf{Timestamps are strictly authored (ratified with schema
major 2).} \texttt{creation\_timestamp} and
\texttt{modification\_timestamp} are stored, \emph{authored} values:
genesis takes the creation timestamp explicitly (the empty-document
constructor defaults it to the epoch-zero unset convention), and a
\texttt{SetMetadata} operation carries both verbatim as part of the
metadata value. Implementations \MUSTNOT{} write either field
implicitly --- an automatic modification-time update would rewrite
canonical bytes on every edit, churning content addresses and breaking
replica byte-agreement for identical operation histories. A client
that wants a live modification time surfaces one from non-canonical
state (the operation log's stamps); it does not store it here.
\end{requirement}
\section{Identifiers}
\label{sec:graph:ids}
@ -4552,6 +4591,48 @@ pub struct StaffInstance {
pub visible: bool,
}
/// The clef content model (ratified schema major 2; the value types
/// landed with the visible slice and supersede the earlier ClefId
/// identifier sketch). A clef is a value, not a registry reference.
pub enum ClefShape {
/// G clef (treble family) -- reference pitch G4.
G,
/// F clef (bass family) -- reference pitch F3.
F,
/// C clef (alto / tenor family) -- reference pitch middle C (C4).
C,
/// Unpitched percussion clef -- no diatonic reference.
Percussion,
}
pub struct Clef {
pub shape: ClefShape,
/// The staff line (1 = bottom) the shape's reference pitch sits on.
pub line: i8,
/// Signed octave displacement (e.g., -1 for the vocal tenor G clef).
pub octave_shift: i8,
}
/// A key signature on the circle of fifths, validated to the
/// conventional CMN range -7 (seven flats) ..= +7 (seven sharps).
/// Constructed only through a checked constructor; decoders reject
/// out-of-range values.
pub struct KeySignature {
fifths: i8, // private; KeySignature::new rejects |fifths| > 7
}
/// A clef change at a point in a staff instance.
pub struct ClefChange {
pub anchor: TimeAnchor,
pub clef: Clef,
}
/// A key-signature change at a point in a staff instance.
pub struct KeySignatureChange {
pub anchor: TimeAnchor,
pub key: KeySignature,
}
/// A per-staff (or per-region-default) metric organization.
pub struct MetricGrid {
/// Sequence of meter changes within the scope of this grid.
@ -4631,18 +4712,70 @@ pub struct Instrument {
/// pitches; not a hard constraint.
pub range: Option<PitchRange>,
/// Default clef and staff line count for staves of this
/// instrument.
pub default_clef: ClefId,
pub default_staff_lines: u8,
/// Default clef and staff line configuration for staves of this
/// instrument. The clef is the content-bearing Clef value defined
/// in this chapter (shape, line, octave shift) -- the value model
/// ratified with the visible slice supersedes the earlier ClefId
/// identifier sketch (reconciled schema major 2).
pub default_clef: Clef,
pub default_staff_lines: StaffLineConfiguration,
/// For unpitched instruments: definitions of each playable
/// member (snare, kick, ride bell, etc.) and their staff
/// positions.
pub unpitched_members: Vec<UnpitchedMember>,
}
/// Opaque sound configuration (defined schema major 2). The audio
/// engine specification owns the structure; the core stores the bytes
/// verbatim and never interprets them. Empty is the default.
pub struct SoundConfiguration(pub Vec<u8>);
/// A written-versus-sounding transposition (defined schema major 2).
/// Structural only: the diatonic and chromatic step counts of the
/// interval (e.g., B-flat clarinet = -1 diatonic, -2 chromatic).
/// Semantically ADVISORY until the Chapter 4 tuning catalog pins
/// interval algebra (the same P12-K2 pinning discipline as the
/// Transpose operation); nothing in the core resolves it to
/// frequencies or respells through it yet.
pub struct TranspositionInterval {
pub diatonic_steps: i32,
pub chromatic_steps: i32,
}
/// One playable member of an unpitched instrument (defined schema
/// major 2): the identifier UnpitchedEvent.instrument_member
/// references, its display name, and its default staff position.
pub struct UnpitchedMember {
pub member: UnpitchedMemberId,
pub name: String,
pub staff_position: StaffPosition,
}
\end{lstlisting}
\texttt{UnpitchedMemberId} is an \emph{instrument-scoped small value}
(a plain 32-bit integer), not a member of the 128-bit typed-identifier
family --- it is minted by the author of the instrument definition, not
by replicas, and participates in no content derivation. Member values
\emph{should} be unique within one instrument's
\texttt{unpitched\_members}; the authoring advisory layer flags a
duplicate, and resolution is by first match in list order (canonical
bytes preserve the authored order), so a duplicate is deterministic,
merely shadowed. An \texttt{UnpitchedEvent.instrument\_member} with no
matching entry resolves to \emph{no member definition}: the event still
renders (its own \texttt{staff\_position} field governs placement ---
the member's \texttt{staff\_position} is the \emph{authoring default}
copied onto new events, not a render-time override), and sound mapping
falls back to the instrument's \texttt{sound\_config}. This
no-match tolerance is load-bearing: every pre-major-2 instrument has an
empty member list while its events carry member values.
\texttt{StaffPosition}'s vertical convention (which integer is which
line, and its orientation) remains \emph{deliberately deferred} to the
Chapter~\ref{ch:layout-ir} vertical model, with the unpitched-rendering
tranche as its landing site; until then the field is preserved and
compared verbatim, and no implementation may claim conformant unpitched
\emph{rendering}.
\subsection{Staves: Identity Versus Instance}
\label{sec:graph:staff-identity}
@ -4676,8 +4809,10 @@ pub struct Staff {
/// overridden per-instance.
pub instrument: InstrumentId,
/// Default clef for new instances of this staff.
pub default_clef: ClefId,
/// Default clef for new instances of this staff (the
/// content-bearing Clef value defined in this chapter; reconciled
/// schema major 2).
pub default_clef: Clef,
/// Default staff line configuration.
pub default_staff_lines: StaffLineConfiguration,
@ -4693,6 +4828,26 @@ pub struct StaffLineConfiguration {
pub line_style: LineStyle,
pub bracket: Option<StaffBracketKind>,
}
/// A dimension in staff spaces (defined schema major 2). Staff line
/// spacing is relative to the global staff space; 1.0 is a
/// normal-size staff, smaller values yield cue/ossia staves.
pub struct SpaceUnit(pub CanonicalF64);
/// A line drawing style (defined schema major 2). Shared by staff
/// lines and the slur/tie/spanner style records below.
pub enum LineStyle {
Solid,
Dashed,
Dotted,
}
/// A per-staff bracket adornment (defined schema major 2), distinct
/// from StaffGroup-level bracketing.
pub enum StaffBracketKind {
Brace,
Bracket,
}
\end{lstlisting}
The score's top-level structure carries a list of \texttt{Staff}
@ -4929,7 +5084,46 @@ pub struct Slur {
pub curvature_override: Option<CurvatureOverride>,
/// Visual style: solid, dashed, dotted; line thickness curve.
pub style: SlurStyle,
pub style: SpanStyle,
}
/// The class of a slur (defined schema major 2). The v1-to-v2
/// migration default is Legato.
pub enum SlurKind {
/// An ordinary legato slur.
Legato,
/// A phrase mark (typically longer, over sub-phrases).
Phrase,
/// An articulation slur (e.g., over a two-note sigh figure).
Articulation,
/// An editorial slur (rendered distinctly, e.g., dashed).
Editorial,
}
/// Which side of the notes a curve arcs toward (defined schema
/// major 2).
pub enum CurveDirection {
Above,
Below,
}
/// An authored curvature override (defined schema major 2). The
/// engraver computes default curvature; each present field overrides
/// that component of it. Consumed by the Standard engraving tier;
/// stored and preserved at every tier.
pub struct CurvatureOverride {
pub direction: Option<CurveDirection>,
/// Arc height at the apex.
pub height: Option<SpaceUnit>,
}
/// The visual style of a spanning mark (defined schema major 2): one
/// shared record for Slur.style, Tie.style, and Spanner.style.
/// Defaults: solid, engraver-chosen thickness.
pub struct SpanStyle {
pub line: LineStyle,
/// Line thickness; None = the engraver's default.
pub thickness: Option<SpaceUnit>,
}
\end{lstlisting}
@ -4957,7 +5151,8 @@ pub struct Tie {
/// cross-voice, or notation-specific use.
pub class: TieClass,
pub style: TieStyle,
/// Visual style (the shared SpanStyle record, schema major 2).
pub style: SpanStyle,
}
pub enum TieClass {
@ -5042,6 +5237,27 @@ pub struct Beam {
/// Beam angle and stem-length overrides.
pub geometry_override: Option<BeamGeometryOverride>,
}
/// A beam segment at a deeper subdivision level (defined schema
/// major 2): a contiguous subset of the owning beam's events beamed
/// together at `level` (strictly deeper than the owning beam's
/// primary level).
pub struct SubBeam {
pub level: u8,
pub events: Vec<EventId>,
}
/// An authored beam-geometry override (defined schema major 2). Each
/// present field overrides the engraver's computed geometry. Consumed
/// by the Standard engraving tier; stored and preserved at every tier.
pub struct BeamGeometryOverride {
/// Beam slope: staff spaces of rise per staff space of run
/// (dimensionless, hence not a SpaceUnit).
pub slope: Option<CanonicalF64>,
/// Vertical displacement of the beam from its default placement
/// (positive = up).
pub offset: Option<SpaceUnit>,
}
\end{lstlisting}
\subsection{Spanners}
@ -5061,10 +5277,16 @@ pub struct Spanner {
/// lines spanning grand staff) target multiple.
pub staves: Vec<StaffId>,
pub style: SpannerStyle,
/// Visual style (the shared SpanStyle record, schema major 2).
pub style: SpanStyle,
}
pub enum SpannerKind {
/// An unclassified spanning mark: renders as a plain line or
/// bracket. First variant deliberately (schema major 2): it is
/// the v1-to-v2 migration default -- a v1 spanner carried no
/// kind, and Generic is the honest translation of that absence.
Generic,
Hairpin(HairpinDirection),
OctaveLine(OctaveOffset),
PedalLine(PedalKind),
@ -5074,6 +5296,37 @@ pub enum SpannerKind {
TextLine(TextLineDefinition),
Bracket(BracketKind),
}
/// Hairpin orientation (defined schema major 2).
pub enum HairpinDirection {
Crescendo,
Diminuendo,
}
/// Octave-line displacement in signed octaves (defined schema
/// major 2): +1 = 8va, -1 = 8vb, +2 = 15ma, -2 = 15mb. Zero is
/// representable but degenerate; the authoring advisory layer flags
/// it, reduction does not.
pub struct OctaveOffset(pub i8);
/// Pedal-line kind (defined schema major 2).
pub enum PedalKind {
Sustain,
Sostenuto,
UnaCorda,
}
/// A text line's content (defined schema major 2); the dash pattern
/// comes from the spanner's style.
pub struct TextLineDefinition {
pub text: String,
}
/// A bracket spanner's shape (defined schema major 2). Growth is by
/// appended variant.
pub enum BracketKind {
Square,
}
\end{lstlisting}
\subsection{Markers}
@ -5122,8 +5375,31 @@ pub enum RepeatKind {
DalSegno { segno: TimeAnchor, end_target: TimeAnchor },
Volta,
}
/// One volta bracket (defined schema major 2): the passes it applies
/// on and the time span it covers. A RepeatStructure of kind Volta
/// carries one entry per ending (e.g., a first ending Volta with
/// endings = [1] and a second with endings = [2]).
pub struct Volta {
/// The pass numbers this ending plays on (1-based), ascending.
pub endings: Vec<u32>,
pub start: TimeAnchor,
pub end: TimeAnchor,
}
\end{lstlisting}
The \texttt{endings} constraints (non-empty, 1-based, strictly
ascending) are \emph{advisory}, on the \texttt{OctaveOffset} pattern:
a violating value is representable, decoders and reduction accept it,
and the authoring validation layer flags it. Rendering treats an
unlistable ending set as a plain bracket.
The v1-to-v2 migration default for a kindless v1 repeat structure is
\texttt{SimpleRepeat} with \texttt{count}~$=$~2: a v1
\texttt{RepeatStructure} \emph{meant} a repeat, and playing the span
twice is the conventional semantics of an unadorned repeat sign.
Voltas default empty.
\subsection{Analytical Annotations}
Analytical annotations record analyses of the music: Roman numerals,
@ -5625,12 +5901,15 @@ construct additional indexes provided they are kept consistent.
representation is defined in Chapter~\ref{ch:layout-ir}.
\item The on-disk serialization of every type introduced in this
chapter is defined in Chapter~\ref{ch:format}.
\item Engraving-specific types (\texttt{StemConfiguration},
\texttt{ClefId}, \texttt{ArticulationMark}, \texttt{ClefChange},
\texttt{KeySignatureChange}, \texttt{LineStyle}, and similar)
are introduced informally here and fully defined in
Chapter~\ref{ch:layout-ir} where their interaction with the
engraver is specified.
\item Some engraving-specific types (\texttt{StemConfiguration},
\texttt{ArticulationMark}, and similar) are introduced informally
here and fully defined in Chapter~\ref{ch:layout-ir} where their
interaction with the engraver is specified. The clef/key content
model (\texttt{Clef}, \texttt{ClefShape}, \texttt{KeySignature},
\texttt{ClefChange}, \texttt{KeySignatureChange}) and
\texttt{LineStyle} are defined normatively \emph{in this chapter}
as of schema major~2; the former \texttt{ClefId} identifier sketch
is retired in favour of embedded \texttt{Clef} values.
\end{itemize}
% ===========================================================================
@ -5871,6 +6150,9 @@ pub enum OperationKind {
// Pitch and tuning operations
RespellPitch(RespellPitchOp),
Transpose(TransposeOp),
InsertIdentifiedPitch(InsertIdentifiedPitchOp),
DeleteIdentifiedPitch(DeleteIdentifiedPitchOp),
ModifyIdentifiedPitch(ModifyIdentifiedPitchOp),
// Cross-cutting operations
CreateCrossCutting(CreateCrossCuttingPayload),
@ -5884,10 +6166,16 @@ pub enum OperationKind {
InsertStaffInstance(InsertStaffInstanceOp),
DeleteStaffInstance(DeleteStaffInstanceOp),
InsertStaff(InsertStaffOp),
CreateVoice(CreateVoiceOp),
DeleteVoice(DeleteVoiceOp),
// Metric-model operations
SetTimeSignature(SetTimeSignatureOp),
SetTempoSegment(SetTempoSegmentOp),
SetMetricGrid(SetMetricGridOp),
// Score settings
SetMetadata(SetMetadataOp),
// Layout-semantic operations
SetUserSystemBreak(SetUserSystemBreakOp),
@ -10789,6 +11077,30 @@ above. It always discards the higher-major non-canonical caches. A major-1
reader migrates a major-0 acceleration snapshot and a major-0
\texttt{CreateRegion} payload on read (default-filling the new fields).
Schema major~2 is the second data-model expansion major: it fills the
truncated bodies of the cross-cutting structures (\texttt{Slur},
\texttt{Tie}, \texttt{Beam}, \texttt{Spanner}), \texttt{RepeatStructure}
(kind and voltas), \texttt{Staff} and \texttt{StaffLineConfiguration},
\texttt{Instrument}, and \texttt{ScoreMetadata}, per this revision's
Chapter~5 definitions. The same per-payload-type rules apply: the
canonical-base \texttt{MaterializedState} embeds none of these values and
stays major~0; the filled values reach the acceleration snapshot
\emph{and}, through the seven operation payloads that embed them
(\texttt{CreateCrossCutting}, \texttt{ModifyCrossCutting},
\texttt{InsertStaff} and \texttt{InsertStaffInstance} (the Operation
Catalog's \texttt{CreateStaff} / \texttt{CreateStaffInstance}),
\texttt{SetStaffLayout}, \texttt{SetMetadata}, and --- transitively,
because an embedded \texttt{Region} may carry a \texttt{StaffInstance}
whose \texttt{staff\_lines\_override} embeds the filled
\texttt{StaffLineConfiguration} --- \texttt{InsertRegion}
(\texttt{CreateRegion})), the canonical operation layer. A block is
stamped with the \emph{lowest} schema major whose layouts decode its
bytes (the companion's minimal-stamping rule --- deterministic, so
identical content hashes identically): any v2-dependent bytes make it
major~2, and a lower-major-only reader opens such a bundle read-only. The wire form
and the total default-filling v1${\to}$v2 migration are defined in the
Binary Format companion.
\section{Format Profiles}
\label{sec:format:profiles}
@ -14997,6 +15309,36 @@ layouts they own versus inherit:
movements: Operation Catalog 0.5.0~$\rightarrow$~0.6.0, Binary Format
0.3.0~$\rightarrow$~0.4.0.
\\
\today & Schema major 2 (data-model expansion, Phase A) &
Defines the leaf types Chapter~5's ratified shapes referenced but never
pinned: \texttt{SlurKind}, \texttt{CurveDirection},
\texttt{CurvatureOverride}, the shared \texttt{SpanStyle} record
(consolidating the formerly-sketched identical
\texttt{SlurStyle}/\texttt{TieStyle}/\texttt{SpannerStyle} triplet),
\texttt{SubBeam}, \texttt{BeamGeometryOverride}, the
\texttt{SpannerKind} payload types (\texttt{HairpinDirection},
\texttt{OctaveOffset}, \texttt{PedalKind}, \texttt{TextLineDefinition},
\texttt{BracketKind}) --- \texttt{SpannerKind}
gains a leading \texttt{Generic} variant as the honest migration default
--- \texttt{Volta}, \texttt{MetadataValue}, \texttt{Timestamp},
\texttt{SoundConfiguration}, \texttt{TranspositionInterval} (structural,
advisory until the Chapter~4 tuning catalog per the P12-K2 discipline),
\texttt{UnpitchedMember}, \texttt{SpaceUnit}, \texttt{LineStyle}, and
\texttt{StaffBracketKind}. Ratifies the clef/key content model into
Chapter~5 (\texttt{Clef}, \texttt{ClefShape}, \texttt{KeySignature},
\texttt{ClefChange}, \texttt{KeySignatureChange} --- the visible-slice
value types) and retires the \texttt{ClefId} identifier sketch:
\texttt{Staff.default\_clef} and \texttt{Instrument.default\_clef} embed
\texttt{Clef} values, and \texttt{Instrument.default\_staff\_lines} is
reconciled from \texttt{u8} to \texttt{StaffLineConfiguration}. Metadata
timestamps are pinned strictly authored
(Requirement~\ref{req:graph:metadata-timestamps}: nothing writes them
implicitly). Chapter~8 gains the schema-major-2 paragraph (canonical base
stays major~0; the filled values reach the snapshot and the seven embedding
operation payloads, \texttt{InsertRegion} transitively). Wire forms and the
total default-filling migration: Binary Format companion
0.4.0~$\rightarrow$~0.5.0.
\\
\bottomrule
\end{longtable}