epiphany/spec/binary_format.tex

3446 lines
169 KiB
TeX

% !TEX program = xelatex
%
% Epiphany --- Binary Format (companion specification)
% Companion to the Core Specification. Compile with XeLaTeX.
%
% This document is versioned independently of the Core Specification
% (independent semver; see the Versioning note in the front matter). Its preamble
% is intentionally a self-contained copy of the core specification's preamble so
% the two documents build independently; factoring a shared preamble file is a
% later cleanup, not a v0.1 deliverable.
\documentclass[11pt,letterpaper]{report}
% ---------------------------------------------------------------------------
% Packages
% ---------------------------------------------------------------------------
\usepackage{fontspec}
\usepackage{geometry}
\geometry{
letterpaper,
top=1.05in,
bottom=1.05in,
left=1.15in,
right=1.15in,
headheight=15pt
}
\usepackage[english]{babel}
\usepackage{microtype}
\usepackage{parskip}
\usepackage{xcolor}
\usepackage{hyperref}
\usepackage{enumitem}
\usepackage{titlesec}
\usepackage{fancyhdr}
\usepackage{booktabs}
\usepackage{array}
\usepackage{longtable}
\usepackage{listings}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{tcolorbox}
\tcbuselibrary{breakable, skins}
% ---------------------------------------------------------------------------
% Color palette (shared with the core specification)
% ---------------------------------------------------------------------------
\definecolor{epiphanyteal}{HTML}{1A4044}
\definecolor{epiphanygold}{HTML}{8E6E2E}
\definecolor{epiphanyink}{HTML}{1F1B16}
\definecolor{epiphanyslate}{HTML}{6B6660}
\definecolor{epiphanycream}{HTML}{F8F4ED}
\definecolor{epiphanymist}{HTML}{ECE8E0}
\definecolor{epiphanycode}{HTML}{2A2520}
\definecolor{epiphanycrimson}{HTML}{7A2424}
\hypersetup{
colorlinks=true,
linkcolor=epiphanyteal,
citecolor=epiphanyteal,
urlcolor=epiphanygold,
pdftitle={Epiphany --- Binary Format},
pdfauthor={The Epiphany Project},
pdfsubject={Binary Format companion for the Epiphany music notation platform},
pdfkeywords={music notation, binary format, serialization, wire format, canonical encoding},
bookmarksnumbered=true,
bookmarksopen=true
}
% ---------------------------------------------------------------------------
% Typography (shared with the core specification)
% ---------------------------------------------------------------------------
\setmainfont{TeX Gyre Pagella}[Numbers={OldStyle, Proportional}, Ligatures={TeX, Common}]
\setsansfont{TeX Gyre Heros}[Scale=0.94, Ligatures={TeX, Common}]
\setmonofont{TeX Gyre Cursor}[Scale=0.88, Ligatures={TeX}]
\newfontfamily\titlefont{TeX Gyre Pagella}[Numbers={OldStyle}, Ligatures={TeX, Common}]
\newcommand{\tablenums}[1]{{\addfontfeatures{Numbers={Lining,Tabular}}#1}}
\newcommand{\sectionsc}[1]{{\addfontfeatures{Letters=SmallCaps}#1}}
% ---------------------------------------------------------------------------
% Section styling (shared with the core specification)
% ---------------------------------------------------------------------------
\titleformat{\chapter}[display]
{\normalfont\filright}
{\raggedright\color{epiphanygold}\fontsize{14pt}{16pt}\selectfont
\scshape Chapter\ \thechapter}
{16pt}
{\raggedright\color{epiphanyteal}\fontsize{32pt}{36pt}\selectfont\bfseries}
[\vspace{4pt}{\color{epiphanygold}\rule{2in}{0.6pt}}]
\titlespacing*{\chapter}{0pt}{-20pt}{30pt}
\titleformat{\section}
{\normalfont\Large\bfseries\color{epiphanyteal}}
{\color{epiphanygold}\thesection}{1em}{}
\titleformat{\subsection}
{\normalfont\large\bfseries\color{epiphanyteal}}
{\color{epiphanygold}\thesubsection}{1em}{}
\titleformat{\subsubsection}
{\normalfont\normalsize\bfseries\color{epiphanyink}}
{\thesubsubsection}{1em}{}
% ---------------------------------------------------------------------------
% Headers and footers (shared with the core specification)
% ---------------------------------------------------------------------------
\pagestyle{fancy}
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}
\fancyhead[L]{\small\scshape\color{epiphanyslate}Epiphany --- Binary Format}
\fancyhead[R]{\small\itshape\color{epiphanyslate}\leftmark}
\fancyfoot[C]{\small\color{epiphanyslate}\thepage}
\renewcommand{\headrule}{
\color{epiphanygold!50}\hrule width\headwidth height 0.4pt
\vspace{1pt}
\color{epiphanygold!30}\hrule width\headwidth height 0.2pt
}
% ---------------------------------------------------------------------------
% Code listing style (shared with the core specification)
% ---------------------------------------------------------------------------
\lstdefinelanguage{Rust}{
keywords={fn,let,mut,pub,struct,enum,impl,trait,for,in,if,else,match,return,
use,mod,crate,self,Self,as,where,move,async,await,const,static,
ref,type,unsafe,extern,dyn,box,break,continue,loop,while},
keywordstyle=\color{epiphanyteal}\bfseries,
ndkeywords={i8,i16,i32,i64,i128,u8,u16,u32,u64,u128,f32,f64,bool,char,str,
String,Vec,Option,Result,Box,Rc,Arc,HashMap,BTreeMap,
NonZeroU16,NonZeroU32,NonZeroU64,Duration,Timestamp},
ndkeywordstyle=\color{epiphanygold}\bfseries,
sensitive=true,
comment=[l]{//},
morecomment=[s]{/*}{*/},
commentstyle=\color{epiphanyslate}\itshape,
stringstyle=\color{epiphanycrimson},
morestring=[b]",
morestring=[b]'
}
\lstset{
basicstyle=\ttfamily\small\color{epiphanycode},
backgroundcolor=\color{epiphanycream},
frame=leftline,
rulecolor=\color{epiphanygold!60},
framesep=8pt,
framerule=1.5pt,
xleftmargin=10pt,
xrightmargin=4pt,
breaklines=true,
showstringspaces=false,
numberstyle=\tiny\color{epiphanyslate},
numbersep=10pt,
captionpos=b,
aboveskip=10pt,
belowskip=10pt,
language=Rust
}
% ---------------------------------------------------------------------------
% Custom environments (shared with the core specification)
% ---------------------------------------------------------------------------
\newtcolorbox{openquestion}[1][]{
enhanced, breakable,
colback=epiphanymist, colframe=epiphanycrimson,
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Open Question},
coltitle=white, colbacktitle=epiphanycrimson,
arc=1pt, boxrule=0pt, leftrule=2pt,
left=10pt, right=10pt, top=8pt, bottom=8pt,
attach boxed title to top left={xshift=0pt, yshift=0pt},
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
#1
}
\newtcolorbox{rationale}[1][]{
enhanced, breakable,
colback=epiphanymist, colframe=epiphanyteal,
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Rationale},
coltitle=white, colbacktitle=epiphanyteal,
arc=1pt, boxrule=0pt, leftrule=2pt,
left=10pt, right=10pt, top=8pt, bottom=8pt,
attach boxed title to top left={xshift=0pt, yshift=0pt},
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
#1
}
% Numbered within chapter (this document has chapters); see core_spec.tex's
% requirement box for why a plain counter + `code=` step is used instead of
% tcolorbox's own "auto counter, number within=..." keys.
\newcounter{requirement}[chapter]
\renewcommand{\therequirement}{\thechapter.\arabic{requirement}}
\newtcolorbox{requirement}[1][]{
enhanced, breakable,
colback=white, colframe=epiphanygold,
fonttitle=\bfseries\color{white}, code={\refstepcounter{requirement}}, title={\scshape\hspace{2pt}Requirement~\therequirement},
coltitle=white, colbacktitle=epiphanygold,
arc=1pt, boxrule=0pt, leftrule=2pt,
left=10pt, right=10pt, top=8pt, bottom=8pt,
attach boxed title to top left={xshift=0pt, yshift=0pt},
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
#1
}
\newtcolorbox{nongoal}[1][]{
enhanced, breakable,
colback=epiphanymist, colframe=epiphanyslate,
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Non-Goal},
coltitle=white, colbacktitle=epiphanyslate,
arc=1pt, boxrule=0pt, leftrule=2pt,
left=10pt, right=10pt, top=8pt, bottom=8pt,
attach boxed title to top left={xshift=0pt, yshift=0pt},
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
#1
}
\newcommand{\MUST}{\textbf{MUST}}
\newcommand{\MUSTNOT}{\textbf{MUST}\nobreak\ \textbf{NOT}}
\newcommand{\SHOULD}{\textbf{SHOULD}}
\newcommand{\SHOULDNOT}{\textbf{SHOULD}\nobreak\ \textbf{NOT}}
\newcommand{\MAY}{\textbf{MAY}}
% Byte concatenation within a wire layout.
\newcommand{\cat}{\ensuremath{\,\Vert\,}}
% A breakable underscore for long monospace anchor names.
\newcommand{\ub}{\_\allowbreak}
% Long monospace tokens (requirement names, test paths) cannot hyphenate;
% allow loose lines instead of overfull ones.
\emergencystretch=3em
\tolerance=2000
\setlist[itemize]{topsep=2pt, itemsep=3pt, parsep=0pt}
\setlist[enumerate]{topsep=2pt, itemsep=3pt, parsep=0pt}
\setlist[description]{topsep=2pt, itemsep=5pt, parsep=0pt}
\AtBeginDocument{\color{epiphanyink}}
% ---------------------------------------------------------------------------
% Document
% ---------------------------------------------------------------------------
\begin{document}
\begin{titlepage}
\thispagestyle{empty}
\centering
\vspace*{2.2in}
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[18pt]
{\titlefont\fontsize{34pt}{38pt}\selectfont\color{epiphanyteal}\bfseries Epiphany}\\[10pt]
{\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.10.0 --- Schema major 3: the tuning context's SMuFL requirement and per-scope overrides reach the wire}\\[4pt]
{\small\color{epiphanyslate}Normative for the byte layouts it defines}
\vfill
\end{titlepage}
\tableofcontents
% ===========================================================================
\chapter{About This Companion}
\label{ch:about}
The \emph{Binary Format} document is a companion to the Epiphany Core
Specification. It fulfils the core specification's delegation in Chapter~8,
\sectionsc{Binary Format Companion} (the \texttt{sec:format:binary} section):
the byte-level encoding --- integer conventions, exact field widths, record
alignment, endianness, string encoding details, struct layouts --- is delivered
here. In the core specification's words: \emph{``The Binary Format document is
normative; an implementation cannot conform to the file format specification
without conforming to the Binary Format specification.''}
This release (v0.1.0) delivers the full \textbf{K-independent} encoding
surface --- identifier and derivation layouts, primitive value encodings, the
whole-\texttt{Score} composite value codec, the operation-layer wire forms,
the bundle physical layout, and the extension-declaration blob forms ---
\emph{plus} the \textbf{K0/M2 operation-payload wire framing}. The Operation
Catalog's K0 set (the representative primitives and the M2 broad-K0 groups,
catalog version 0.4.0) now exists in ratified form, so the historical ``waits
for Agent~K'' clause in the Phase-2 charter is discharged: every operation
kind the catalog defines has its literal wire form pinned in
Chapter~\ref{ch:ops}.
This document does \emph{not} cover:
\begin{itemize}
\item the canonical s-expression form --- that is the \emph{Text Projection}
companion's (version~0.1.0, which now exists);
\item per-profile feature lists --- the \emph{Profile Conformance}
companion's;
\item the Chapter-4 tuning-catalog values (pitch-space and tuning-system
registries), which are undelivered Track-C work and have no wire form yet;
\item the wire-format fuzzer, an \emph{implementation} deliverable tracked
in the Phase-2 process documents rather than part of this document's
normative text; Chapter~\ref{ch:goldens} records the golden anchors any
such harness must reproduce.
\end{itemize}
It \emph{does} cover the cross-implementation decoder test, which is no longer
deferred: Section~\ref{sec:goldens:decode-vectors} ratifies the literal-byte
vector corpus and the accept/reject verdict a conforming decoder owes it.
\section{Relationship to the Core Specification and the Operation Catalog}
\label{sec:about:relationship}
This companion \emph{inherits} --- it never re-derives --- the core
specification's ratified convention baseline: core specification Chapter~8,
\sectionsc{Binary Format Companion}, requirement
\texttt{req:format:codec-conventions}, together with the
\sectionsc{Canonical Byte-Layout Reference} appendix (\texttt{app:bytes}).
That appendix consolidates every discriminant table, derivation preimage, and
primitive encoding Pass~11 and Pass~12 ratified, precisely so that this
document could import it as a starting point rather than recovering the
layouts from the crates. Chapter~\ref{ch:ids} therefore reproduces only
\emph{compact} normative tables, each with a citation back to the appendix
row and requirement that governs it; where this document and a ratified core
requirement disagree, \textbf{the core requirement governs} and the
discrepancy is a defect in this document.
What this document adds is the completion of the appendix's deferred list
(core specification, \sectionsc{Layouts deferred to the companions},
\texttt{sec:bytes:deferred}): the \texttt{OperationKind} and
\texttt{OperationKindTag} discriminants and full per-operation payload
encodings; the full composite struct layouts (whole-\texttt{Score}, operation
envelopes, the manifest body's field order); schema-version wire evolution;
and the varint question, which Chapter~\ref{ch:conventions} settles.
The division of labour with the \emph{Operation Catalog} is exact: the
catalog fixes each operation's \textbf{field set and order} (Operation
Catalog, \sectionsc{Per-Primitive Schema Template}) and its semantics; this
document pins the \textbf{bytes} of those fields. Chapter~\ref{ch:ops} cites
the catalog section for every payload rather than restating what the fields
mean. Likewise, graph \emph{types} are the core specification's Chapters 2--5,
and the structural layer of the bundle --- what a header, superblock, chunk
graph, or manifest \emph{is} and how it behaves --- is core Chapter~8; this
document specifies only how those structures' bits are laid out.
\begin{rationale}
\textbf{Versioning.} This companion is versioned independently of the core
specification (independent semver), like the Operation Catalog --- but tied
tighter to it: the binary-format \textsc{major} version tracks the core
specification's major version (both are pre-1.0 working drafts today, hence
v0.1.0), while \textsc{minor} versions diverge freely. Byte layouts change on
a slower, more deliberate cadence than prose; a core-spec minor revision that
touches no wire fact requires no revision here, and an additive discriminant
append here (Chapter~\ref{ch:evolution}) requires no core-spec revision.
\end{rationale}
\section{Conformance}
\label{sec:about:conformance}
The byte layouts in this document are \textbf{normative}. An implementation
conforms to the Epiphany binary format if and only if, for every layout this
document defines, it produces exactly the specified bytes when encoding and
accepts exactly the specified language of byte strings when decoding.
Decode discipline is \textbf{reject, never normalize} --- the workspace-wide
convention the core specification establishes in Appendix~D (canonical
serialization determinism and ordered iteration). Concretely, each of the
following is a \emph{decode error}, never an input to be repaired:
\begin{itemize}
\item an unknown discriminant in any tagged union;
\item a set- or map-valued field whose elements are not in strictly
ascending canonical order, or which contains duplicates;
\item text that is not in Unicode NFC where NFC is required (catalog
identifiers, barrier pitch-space names, operation labels);
\item trailing bytes after a complete top-level value
(Requirement~\ref{req:binfmt:trailing-bytes});
\item recursion beyond a normative depth bound
(Requirement~\ref{req:binfmt:condition-depth}).
\end{itemize}
A decoder \MUSTNOT{} silently sort, deduplicate, NFC-fold, truncate, or
otherwise canonicalize non-canonical input: two implementations that disagree
on repair would disagree on canonical state.
The \textbf{golden anchor registry} (Chapter~\ref{ch:goldens}) is the
conformance contract binding this text to the reference implementation: every
layout in this document is anchored by at least one named test in the
reference crates, and a change that breaks an anchor is either a defect or a
deliberate, versioned format revision (Chapter~\ref{ch:evolution}).
% ===========================================================================
\chapter{Encoding Conventions}
\label{ch:conventions}
This chapter restates the ratified convention baseline (core specification,
requirement \texttt{req:format:codec-conventions}) as normative fact and
completes it: the three prefix/endianness regimes, the primitive composition
rules, the varint disposition, and the trailing-bytes rule.
\section{The Three Encoding Regimes}
\label{sec:conventions:regimes}
Three internally consistent regimes coexist in the format. Every layout in
this document belongs to exactly one of them, and each layout's chapter says
which.
\begin{longtable}{p{0.55in} p{2.25in} p{2.85in}}
\toprule
\textbf{Regime} & \textbf{Surfaces} & \textbf{Rules} \\
\midrule
\endhead
(a) & Canonical document encodings: the core value codec
(Chapter~\ref{ch:values}), the operation layer
(Chapter~\ref{ch:ops}), the bundle codec
(Chapter~\ref{ch:bundle}). &
Little-endian fixed-width integers; \texttt{u32} little-endian counts
and length prefixes on every variable-width field. \\
(b) & Layout-side pinned encodings: the extension-declaration blobs and
edit-barrier trees (Chapter~\ref{ch:barriers}) and the
\texttt{ResolvedLayoutIR} canonical output
(Chapter~\ref{ch:noncanon}). &
Little-endian integers; \texttt{u64} little-endian counts and length
prefixes --- a golden-locked divergence from regime (a). Schema major~1
moves the \emph{resolved-layout} surface to \texttt{u32}; the
manifest-embedded barrier blobs stay \texttt{u64}
(Section~\ref{sec:evolution:major1}). \\
(c) & Identifiers and hash-derived forms: the 128-bit identifier family,
\texttt{ReplicaId}, \texttt{OperationId}, \texttt{ConflictId}, the
\texttt{TypedObjectId} discriminant, digest truncations
(Chapter~\ref{ch:ids}). &
Big-endian fixed width. Numeric order equals canonical byte order,
which is what Appendix~D's ordered iteration requires. \\
\bottomrule
\end{longtable}
\begin{rationale}
In schema major~0, regimes (a) and (b) differ only in prefix width, and the
divergence was \emph{tolerated rather than repaired}: the two surfaces were
golden-locked independently (the barrier blobs and the resolved-layout output
carry their own literal-byte anchors), they never embed one another's framing,
and unifying the prefix width within major~0 would break locked bytes for zero
benefit. Once a major bump was scheduled for data-model reasons, part of the
unification became free to carry: schema major~1 moves the resolved-layout
surface (an independent non-canonical \texttt{LayoutCache}) to regime (a)'s
\texttt{u32} prefixes and re-locks its goldens. The barrier blobs cannot follow
in the same step --- they are carried opaquely inside the \emph{canonical}
manifest, which stays major~0 --- so they keep regime (b) until a manifest-
revising major (Chapter~\ref{ch:evolution}). Regime (c) is not a divergence at
all: an identifier's byte form \emph{is} its sort key, so big-endian is
load-bearing.
\end{rationale}
\section{Primitive Composition Rules}
\label{sec:conventions:primitives}
The following rules apply throughout regime (a); regimes (b) and (c) state
their deltas in their own chapters.
\begin{description}
\item[Booleans.] A single byte: \texttt{0} = false, \texttt{1} = true. Any
other value is a decode error.
\item[Integers.] Fixed-width little-endian two's complement
(\texttt{u8}/\texttt{i8} through \texttt{u128}), except within regime (c).
\item[\texttt{Option<T>}.] A presence byte: \texttt{0} = absent (no further
bytes), \texttt{1} = present, followed by \texttt{T}'s encoding. A
presence byte greater than~1 is a decode error.
\item[Sequences, sets, maps.] A \texttt{u32} little-endian element count,
then each element (for maps: key then value). Set- and map-valued fields
are emitted in canonical iteration order --- ascending by the key's
canonical byte form --- and decoders reject out-of-order or duplicated
elements wherever the field is a set or map (as opposed to an
order-significant list).
\item[Free text.] A \texttt{u32} little-endian byte length, then the raw
UTF-8 bytes. Free-text fields (e.g.\ a score title) are \textbf{never}
NFC-folded by the codec: $\mathrm{decode}(\mathrm{encode}(x)) = x$ is the
round-trip identity, and folding would break it. By contrast,
\emph{catalog identifiers} (pitch-space ids, tuning-system ids, and the
other \texttt{catalog\_id} newtypes) are NFC-normalized \emph{at
construction}, so their stored form is already NFC; the codec encodes the
stored string verbatim. Operation-layer text fields (the transaction
label, conflict field paths) are NFC-normalized at encode time
(Chapter~\ref{ch:ops}).
\item[Structs.] Positional, unframed concatenation of the fields' encodings
in declaration order. No field tags, no per-field lengths, no padding, no
alignment. (This is the layout the frozen-layout rule,
Requirement~\ref{req:binfmt:frozen-layout}, freezes.)
\item[Tagged unions.] A single discriminant byte, then the selected
variant's payload encoded positionally. Two documented exceptions:
\texttt{TypedObjectId} uses a \emph{16-bit big-endian} discriminant
(core requirement \texttt{req:graph:typed-object-id-discriminants};
Chapter~\ref{ch:ids}), and the bundle's \texttt{ProfileId} uses a
\texttt{u32} little-endian discriminant (core requirement
\texttt{req:format:profileid-discriminants}; Chapter~\ref{ch:bundle}).
\item[Determinism-canonical leaves.] Where a composite codec embeds a value
whose canonical byte form is owned by the determinism layer or the
identifier layer (a graph identifier, \texttt{ContentHash},
\texttt{CanonicalF64}, \texttt{RationalTime}, a wall-clock integer), the
core value codec frames it as a \texttt{u32} little-endian length prefix
followed by the leaf's own canonical bytes --- even when the leaf is
fixed-width. The leaf's bytes are then self-describing under its own
decoder. (The operation layer instead embeds fixed-width leaves
\emph{raw}, unprefixed; Chapter~\ref{ch:ops} states this explicitly.)
\end{description}
\begin{requirement}
\label{req:binfmt:no-varint}
\textbf{No varint.} Schema major~0 defines \emph{no} variable-length integer
encoding. Every integer field in every layout in this document is
fixed-width. The core specification's ``varint conventions'' language
(Chapter~8, \sectionsc{Binary Format Companion}) is hereby discharged as
\emph{none}: an encoder \MUSTNOT{} emit, and a decoder \MUSTNOT{} accept, any
LEB128-style or otherwise variable-width integer under schema major~0.
Variable width in this format comes only from explicit \texttt{u32}/%
\texttt{u64} length prefixes and from \texttt{RationalTime}'s length-prefixed
big-integer magnitudes (Chapter~\ref{ch:primitives}).
\end{requirement}
\begin{requirement}
\label{req:binfmt:trailing-bytes}
\textbf{Trailing bytes.} Every top-level decode entry point --- a
whole-\texttt{Score} value, a per-value \texttt{CanonicalValue} decode, an
\texttt{OperationKindTag}, a \texttt{MaterializedState}, a manifest payload,
an operation-index payload, a block payload, an extension-declaration blob, a
fixed-width primitive --- \MUST{} reject input with bytes remaining after the
complete value has been decoded. Trailing bytes are a decode error, never
ignored padding.
\end{requirement}
% ===========================================================================
\chapter{Identifiers and Derivations}
\label{ch:ids}
This chapter is an \emph{import-by-citation} of the core specification's
\sectionsc{Canonical Byte-Layout Reference} appendix (\texttt{app:bytes}):
that appendix and its golden anchors govern every table in this chapter, and
the tables here are compact restatements for self-containedness, not a second
source of truth. All layouts in this chapter are regime (c): big-endian,
fixed width.
\section{The 128-Bit Identifier Family}
\label{sec:ids:family}
\begin{description}
\item[\texttt{ReplicaId}.] 8 bytes, big-endian \texttt{u64}. The reserved
system namespace is \texttt{ReplicaId::SYSTEM\ub DERIVED} =
\texttt{0xFFFF\ub FFFF\ub FFFF\ub FFFF}; user-authored replicas
\MUSTNOT{} use it.
\item[Typed graph identifiers.] Every typed 128-bit graph identifier
(\texttt{EventId}, \texttt{PitchId}, \texttt{VoiceId},
\texttt{RegionId}, \texttt{StaffInstanceId}, \texttt{TransactionId},
\ldots{} --- the \texttt{graph\_id} family) packs the 64-bit replica
into the high half and the counter into the low half, and encodes as
exactly 16~bytes: replica big-endian (8) \cat{} counter big-endian (8).
Numeric order, canonical byte order, and Appendix-D iteration order
coincide.
\item[\texttt{OperationId}.] The same 16-byte form: replica big-endian (8)
\cat{} counter big-endian (8). Ordering is $(\mathit{replica},
\mathit{counter})$ lexicographic, which equals byte order.
\item[Registry and author identifiers (operation layer).] The opaque
128-bit registry newtypes (\texttt{OperationKind\allowbreak RegistryId},
\texttt{ConflictKind\allowbreak RegistryId},
\texttt{Resolution\allowbreak RegistryId},
\texttt{RepairKind\allowbreak RegistryId},
\texttt{ReanchorReason\allowbreak RegistryId},
\texttt{ReplicaAnomaly\allowbreak RegistryId},
\texttt{IntegrityAnomaly\allowbreak RegistryId},
\texttt{Extension\allowbreak PreconditionId},
\texttt{PreconditionFailure\allowbreak RegistryId}) and \texttt{AuthorId}
all encode as 16 big-endian bytes.
\end{description}
\section{\texttt{TypedObjectId}}
\label{sec:ids:typed-object-id}
Canonical form: a \textbf{16-bit big-endian discriminant} \cat{} the variant
payload (core requirement \texttt{req:graph:typed-object-id-discriminants}).
Non-\texttt{Registered} variants carry one 16-byte identifier, for a total of
\textbf{18~bytes}; \texttt{Registered} carries the
\texttt{ObjectKindRegistryId} (16 big-endian bytes) \cat{} the extension's
own raw \texttt{u128} (16 big-endian bytes), for a total of \textbf{34~bytes}.
An unknown discriminant is a decode error.
\begingroup\small
\begin{longtable}{p{0.5in} p{1.6in} p{0.5in} p{1.6in}}
\toprule
\textbf{Disc} & \textbf{Variant} & \textbf{Disc} & \textbf{Variant} \\
\midrule
\endhead
\tablenums{0} & \texttt{Event} & \tablenums{14} & \texttt{Spanner} \\
\tablenums{1} & \texttt{Pitch} & \tablenums{15} & \texttt{Marker} \\
\tablenums{2} & \texttt{Voice} & \tablenums{16} & \texttt{AnalyticalAnnotation} \\
\tablenums{3} & \texttt{Staff} & \tablenums{17} & \texttt{Comment} \\
\tablenums{4} & \texttt{StaffInstance} & \tablenums{18} & \texttt{GraphicObject} \\
\tablenums{5} & \texttt{StaffGroup} & \tablenums{19} & \texttt{GraphicGesture} \\
\tablenums{6} & \texttt{Region} & \tablenums{20} & \texttt{TimeSignature} \\
\tablenums{7} & \texttt{Instrument} & \tablenums{21} & \texttt{AnalysisLayer} \\
\tablenums{8} & \texttt{PartDefinition} & \tablenums{22} & \texttt{Tuplet} \\
\tablenums{9} & \texttt{Measure} & \tablenums{23} & \texttt{RepeatStructure} \\
\tablenums{10} & \texttt{BarlineAlignmentGroup} & \tablenums{24} & \texttt{LyricLine} \\
\tablenums{11} & \texttt{Slur} & \tablenums{25} & \texttt{ChordSymbol} \\
\tablenums{12} & \texttt{Tie} & \tablenums{26} & \texttt{View} \\
\tablenums{13} & \texttt{Beam} & \tablenums{27} & \texttt{Registered} \\
\bottomrule
\end{longtable}
\endgroup
\section{System-Derived Counters and Digest Truncations}
\label{sec:ids:derivations}
All content derivation uses BLAKE3-256 over a domain-separated preimage: the
8-byte domain tag is always the first bytes hashed. Two truncations recur,
both taking the digest's \emph{leading} bytes big-endian:
\begin{itemize}
\item $\mathrm{trunc64}(d) = \mathtt{u64::from\_be\_bytes}(d[0..8])$ ---
the counter of a system-derived identifier;
\item $\mathrm{trunc128}(d) = \mathtt{u128::from\_be\_bytes}(d[0..16])$ ---
content-derived 128-bit identifiers.
\end{itemize}
The system-derived counter function is
$\mathrm{trunc64}(\mathrm{BLAKE3}(\mathit{tag} \cat \mathit{inputs}))$; the
resulting identifier is
$\texttt{from\_parts}(\texttt{ReplicaId::SYSTEM\_DERIVED}, \mathit{counter})$.
Only a system domain tag (prefix \texttt{MUSCS}) may seed it. The ratified
instances (governed by \texttt{app:bytes} and the cited requirements):
\begingroup\small
\begin{longtable}{p{1.35in} p{0.7in} p{3.5in}}
\toprule
\textbf{Identifier} & \textbf{Tag} & \textbf{Canonical inputs} \\
\midrule
\endhead
Promoted \texttt{VoiceId} & \texttt{MUSCSVCE} &
64-byte preimage: \texttt{staff\_instance} (16 BE) \cat{}
\texttt{original\_voice} (16 BE) \cat{} \texttt{winning\_op} (16 BE)
\cat{} \texttt{losing\_op} (16 BE). Core \sectionsc{System-Promoted
Voices}. \\
System \texttt{PitchId} & \texttt{MUSCSPCH} &
The pitch's intrinsic canonical bytes (core requirement
\texttt{req:graph:system-derived-pitch-id}). \\
\texttt{IntegrityAnomalyId} & \texttt{MUSCSANM} &
The anomaly kind's canonical bytes (Chapter~\ref{ch:ops}; core
requirement \texttt{req:graph:integrity-anomaly-id}). \\
\bottomrule
\end{longtable}
\endgroup
The 128-bit content-derived identifiers:
\begingroup\small
\begin{longtable}{p{1.1in} p{0.75in} p{3.7in}}
\toprule
\textbf{Identifier} & \textbf{Tag} & \textbf{Preimage after the tag} \\
\midrule
\endhead
\texttt{ConflictId} & \texttt{MUSCCONF} &
\texttt{kind.canonical\_bytes()} \cat{} causing operations (each 16 BE,
sorted by canonical bytes) \cat{} affected objects (each
\texttt{TypedObjectId} canonical bytes, sorted); $\mathrm{trunc128}$.
Wire form of the id itself: 16 big-endian bytes. \\
\texttt{ManifestId} & \texttt{MUSCMNIF} &
\texttt{document\_id} (16) \cat{} \texttt{generation} (\texttt{u64} LE)
\cat{} manifest body bytes (body excludes the \texttt{manifest\_id}
field); $\mathrm{trunc128}$. Restated normatively in
Chapter~\ref{ch:bundle} (core requirement
\texttt{req:format:manifest-id}). Note the \emph{on-disk} field encodes
little-endian (Chapter~\ref{ch:bundle}). \\
\texttt{EnvelopeHash} & \texttt{MUSCENVH} &
The envelope's complete canonical bytes; the full 32-byte digest (no
truncation). Chapter~\ref{ch:ops}. \\
\texttt{LayoutObjectId} & \texttt{MUSCLOID} &
Non-canonical; three preimage shapes, Chapter~\ref{ch:noncanon}
(core requirement \texttt{req:layoutir:object-id-derivation}). \\
\texttt{BlobId} & \texttt{MUSCBLOB} &
The blob payload, bare (\texttt{req:format:blob-hash-shape}); full
32-byte digest. Chapter~\ref{ch:primitives}. \\
\bottomrule
\end{longtable}
\endgroup
\section{The Domain-Tag Registry}
\label{sec:ids:tags}
Every domain tag is exactly 8 printable-ASCII bytes beginning \texttt{MUSC};
the vocabulary is closed (governed by \texttt{app:bytes},
\sectionsc{Domain-tag registry}). Extension system tags \MUST{} begin
\texttt{MUSCS}, be exactly 8 bytes, and collide with neither the built-ins
nor the file magics.
\begingroup\small
\begin{longtable}{p{1.05in} p{1.55in} p{3.0in}}
\toprule
\textbf{Tag} & \textbf{Name} & \textbf{Use} \\
\midrule
\endhead
\texttt{MUSCCHNK} & chunk & non-manifest chunk hash preimages \\
\texttt{MUSCMANI} & manifest & manifest chunk hash preimages \\
\texttt{MUSCBLOB} & blob & \texttt{BlobId} (bare $\mathit{tag} \cat \mathit{payload}$) \\
\texttt{MUSCCONF} & conflict & \texttt{ConflictId} derivation \\
\texttt{MUSCENVH} & envelope & \texttt{EnvelopeHash} \\
\texttt{MUSCFNTM} & font metrics & glyph-catalog metrics hash (non-canonical) \\
\texttt{MUSCMNIF} & manifest id & \texttt{ManifestId} derivation \\
\texttt{MUSCSVCE} & system voice & promoted-voice counters \\
\texttt{MUSCSPCH} & system pitch & system-derived pitch counters \\
\texttt{MUSCSANM} & system anomaly & \texttt{IntegrityAnomalyId} \\
\texttt{MUSCLOID} & layout object id & \texttt{LayoutObjectId} (non-canonical) \\
\midrule
\texttt{MUSCBND\textbackslash0} & bundle magic & file magic, \emph{not} a hash tag (note the trailing NUL) \\
\texttt{MUSCSUPR} & superblock magic & slot magic, \emph{not} a hash tag \\
\bottomrule
\end{longtable}
\endgroup
\section{\texttt{SnapshotId}}
\label{sec:ids:snapshot-id}
The core specification's support-type identity table says \emph{``the Binary
Format companion defines the exact derivation''} of \texttt{SnapshotId}. v0
has no snapshot producer --- pruning and canonical-base creation are deferred
--- so there is nothing to derive \emph{from} yet.
\begin{requirement}
\label{req:binfmt:snapshot-id-opaque}
In schema major~0, \texttt{SnapshotId} is an \textbf{opaque 16-byte
identifier}: 16 raw bytes on the wire (Chapter~\ref{ch:primitives}), with no
normative derivation function. Readers \MUST{} treat two
\texttt{SnapshotId}s as equal iff their 16 bytes are equal and \MUSTNOT{}
attempt to derive or verify a snapshot id from snapshot content.
\end{requirement}
\begin{openquestion}
The \texttt{SnapshotId} derivation (presumably a content derivation in the
\texttt{trunc128} family, over the snapshot's canonical payload and its
covering frontier) is deferred together with the pruning/garbage-collection
track that will produce the first snapshots. It must be pinned before any
writer emits a \texttt{canonical\_base}; until then the opaque reading above
is complete.
\end{openquestion}
% ===========================================================================
\chapter{Primitive Value Encodings}
\label{ch:primitives}
The leaf encodings owned by the determinism layer and the small fixed
identifier types. These byte forms are what the framings of
Chapters~\ref{ch:values}--\ref{ch:bundle} embed.
\begingroup\small
\begin{longtable}{>{\raggedright\arraybackslash}p{1.7in} p{0.5in} p{3.45in}}
\toprule
\textbf{Type} & \textbf{Width} & \textbf{Encoding} \\
\midrule
\endhead
\texttt{CanonicalF64} & 8 &
IEEE-754 binary64, little-endian. $-0.0$ is canonicalized to $+0.0$ at
construction, so the negative-zero bit pattern never appears in canonical
bytes. Decode rejects NaN and $\pm\infty$ (non-finite floats are
corruption). \\
\texttt{QuantizedCoord} & 8 &
\texttt{i64} little-endian, in units of $1/1024$ staff space
(\texttt{STAFF\_SPACE\_GRID} = 1024). Construction from a float rounds
ties-to-even and rejects non-finite or out-of-range input. \\
\texttt{ContentHash} & 32 & Raw BLAKE3-256 digest bytes. \\
\texttt{ChunkId} & 32 & Raw digest bytes (a \texttt{ContentHash} newtype). \\
\texttt{DomainTag} & 8 &
The raw ASCII tag bytes. Decode validates against the closed vocabulary
of Section~\ref{sec:ids:tags}; foreign bytes are a decode error. \\
\texttt{WallClockTime} & 8 &
\texttt{i64} little-endian, nanoseconds. Floating-point wall-clock time
is forbidden in stored data. \\
\texttt{WallClockDuration} & 8 & \texttt{i64} little-endian, nanoseconds. \\
\texttt{SchemaVersion} & 4 &
\texttt{major} \texttt{u16} LE \cat{} \texttt{minor} \texttt{u16} LE. \\
\texttt{SemVer} & 12 &
\texttt{major} \texttt{u32} LE \cat{} \texttt{minor} \texttt{u32} LE
\cat{} \texttt{patch} \texttt{u32} LE. Canonical \emph{ordering} of
\texttt{SemVer}-keyed collections is \textbf{numeric} on
$(\mathit{major},\mathit{minor},\mathit{patch})$, never byte-lexicographic
on the little-endian encoding (which would order 256.0.0 before 1.0.0). \\
\texttt{Reduction\allowbreak AlgorithmVersion} & 4 & \texttt{u32}
little-endian. \\
\texttt{FileUuid}, \texttt{DocumentId}, \texttt{LineageId},
\texttt{SnapshotId}, \texttt{ExtensionId}, \texttt{ProfileRegistryId} & 16 &
16 raw opaque bytes each. No internal structure is interpreted;
equality is byte equality. \\
\texttt{BlobId} & 32 &
Raw digest bytes: $\mathrm{BLAKE3}(\texttt{MUSCBLOB} \cat
\mathit{payload})$, the only bare $\mathit{tag} \cat \mathit{payload}$
content hash in the format (core requirement
\texttt{req:format:blob-hash-shape}). \\
\texttt{FrontierBytes} & var &
\texttt{u32} LE length \cat{} opaque bytes (an ops-computed causal
frontier the bundle carries verbatim). \\
\bottomrule
\end{longtable}
\endgroup
\section{\texttt{RationalTime}}
\label{sec:primitives:rationaltime}
Restating core requirement \texttt{req:format:rationaltime-encoding}, which
governs. \texttt{RationalTime} is an arbitrary-precision rational, always
stored \emph{reduced} (equal rationals encode identically; the sign lives on
the numerator; the denominator is strictly positive). Its canonical byte
form:
\begin{center}
\begin{tabular}{l l}
\toprule
\textbf{Field} & \textbf{Encoding} \\
\midrule
sign & 1 byte: \tablenums{0} = zero, \tablenums{1} = positive,
\tablenums{2} = negative \\
numerator & \texttt{u32} LE magnitude byte length \cat{} big-endian
magnitude bytes \\
denominator & \texttt{u32} LE magnitude byte length \cat{} big-endian
magnitude bytes \\
\bottomrule
\end{tabular}
\end{center}
Decode rejects a sign byte greater than~2, a zero denominator, and trailing
bytes. \texttt{MusicalPosition} and \texttt{MusicalDuration} are newtypes
whose canonical bytes are exactly their inner \texttt{RationalTime}'s.
Wall-clock integers deliberately do \emph{not} use this form: they are the
fixed-width little-endian integers of the table above, matching
\texttt{QuantizedCoord}'s convention.
% ===========================================================================
\chapter{Graph Value Layouts}
\label{ch:values}
This chapter defines the schema-major-0 wire form of every value reachable
from a \texttt{Score} --- the whole-document composite codec. The core
specification's \texttt{DECISIONS} record (P11-4) held this codec
\emph{provisional} pending this companion;
Requirement~\ref{req:binfmt:score-envelope} and
Requirement~\ref{req:binfmt:frozen-layout} \textbf{ratify} it: the reference
implementation's layout, as specified here, \emph{is} the schema-major-0 wire
form, and P11-4's provisional status is discharged.
All layouts in this chapter are regime (a): little-endian, \texttt{u32}
prefixes.
\section{The \texttt{Score} Envelope}
\label{sec:values:score}
\begin{requirement}
\label{req:binfmt:score-envelope}
The canonical bytes of a \texttt{Score} are the bare positional
concatenation of its nineteen fields' encodings, in exactly this order:
\begin{enumerate}
\item \texttt{metadata}
\item \texttt{canvas}
\item \texttt{instruments}
\item \texttt{staves}
\item \texttt{staff\_groups}
\item \texttt{parts}
\item \texttt{cross\_cutting}
\item \texttt{time\_signatures}
\item \texttt{tuning\_context}
\item \texttt{tempo\_map}
\item \texttt{events}
\item \texttt{spelling\_attachments}
\item \texttt{decomposition\_attachments}
\item \texttt{spelling\_precedence}
\item \texttt{analysis\_layers}
\item \texttt{views}
\item \texttt{identity}
\item \texttt{tombstoned\_pitches}
\item \texttt{tombstoned\_events}
\end{enumerate}
There is \textbf{no magic} and \textbf{no version prefix} at the value
level: format identification lives in the bundle's fixed header
(Chapter~\ref{ch:bundle}) and versioning lives at the chunk layer
(\texttt{SchemaVersion} in every \texttt{ChunkRef} and superblock;
Chapter~\ref{ch:evolution}). Decoders \MUST{} reject trailing bytes after
the nineteenth field.
\end{requirement}
\section{Composition Rules as Normative Encoding Facts}
\label{sec:values:composition}
The reference codec is built from a small set of conventions; each is a
normative statement about the bytes, not an implementation detail:
\begin{description}
\item[Positional structs.] Every struct in this chapter encodes as its
fields in declaration order, unframed
(Section~\ref{sec:conventions:primitives}). Zero-field structs encode as
zero bytes.
\item[C-style enums.] A fieldless enum is a single discriminant byte
(tables in Section~\ref{sec:values:discriminants}).
\item[Tagged unions.] A discriminant byte, then the variant's fields
positionally.
\item[Leaf framing.] The determinism-canonical leaf types ---
\texttt{ReplicaId}, \texttt{OperationId}, every embedded typed graph
identifier (\texttt{EventId}, \texttt{PitchId}, \texttt{VoiceId},
\texttt{StaffId}, \texttt{StaffInstanceId}, \texttt{StaffGroupId},
\texttt{RegionId}, \texttt{InstrumentId}, \texttt{PartDefinitionId},
\texttt{MeasureId}, \texttt{BarlineAlignmentGroupId}, \texttt{SlurId},
\texttt{TieId}, \texttt{BeamId}, \texttt{SpannerId}, \texttt{TupletId},
\texttt{MarkerId}, \texttt{AnalyticalAnnotationId}, \texttt{CommentId},
\texttt{RepeatStructureId}, \texttt{LyricLineId}, \texttt{ChordSymbolId},
\texttt{GraphicObjectId}, \texttt{GraphicGestureId},
\texttt{TimeSignatureId}, \texttt{AnalysisLayerId}, \texttt{ViewId}) ---
plus \texttt{ContentHash}, \texttt{CanonicalF64}, \texttt{RationalTime},
\texttt{MusicalPosition}, \texttt{MusicalDuration},
\texttt{WallClockTime}, and \texttt{WallClockDuration}, are each emitted
as \texttt{u32} LE length \cat{} the leaf's canonical bytes
(Chapters~\ref{ch:ids} and \ref{ch:primitives}). An embedded 16-byte
identifier therefore occupies 20 bytes inside a \texttt{Score}. Decode
runs the leaf's own validating decoder over the framed region.
\item[Catalog identifiers.] The \texttt{catalog\_id} newtypes
(\texttt{PitchSpaceId}, \texttt{TuningSystemId}, \texttt{AccidentalId},
registry-name ids, \ldots) encode as their stored NFC string under the
String rule (\texttt{u32} LE length \cat{} UTF-8).
\item[\texttt{EventArena}.] \texttt{u32} LE event count, then each
\texttt{Event} in ascending-\texttt{EventId} order (identity travels
inside each event; the arena adds no per-event framing).
\item[Validated reconstruction.] Types with checked constructors decode
through them: \texttt{TupletRatio} rejects degenerate ratios,
\texttt{KeySignature} encodes its \texttt{fifths()} as \texttt{i8} and
rejects out-of-range values, \texttt{TimeSignature} re-validates that
beat groups sum to the measure duration, \texttt{NonZeroU16} rejects
zero, \texttt{EventOrderingDAG} rejects cyclic edge maps,
\texttt{ReferencePitch}, \texttt{Tempo}, and
\texttt{SpellingPrecedence} re-validate their invariants. Structurally
well-formed bytes that fail a type invariant are a decode error
(reject-never-normalize).
\end{description}
\section{Discriminant Tables}
\label{sec:values:discriminants}
The complete discriminant assignment for schema major~0. Every table is
governed by Requirement~\ref{req:binfmt:frozen-layout}: an unknown
discriminant is a decode error; changes are gated by
Chapter~\ref{ch:evolution}.
\subsection{C-style enums (one byte, no payload)}
\begingroup\small
\begin{longtable}{p{1.9in} p{3.7in}}
\toprule
\textbf{Type} & \textbf{Discriminants} \\
\midrule
\endhead
\texttt{MeasurePosition} & \tablenums{0} Start, \tablenums{1} End \\
\texttt{RegionEdge} & \tablenums{0} Start, \tablenums{1} End \\
\texttt{CmnNominal} & \tablenums{0} C, \tablenums{1} D, \tablenums{2} E,
\tablenums{3} F, \tablenums{4} G, \tablenums{5} A, \tablenums{6} B \\
\texttt{SpellingSourceKind} & \tablenums{0} UserChosen,
\tablenums{1} Imported, \tablenums{2} Propagated, \tablenums{3} Inferred,
\tablenums{4} Analytical \\
\texttt{TempoShape} & \tablenums{0} Constant, \tablenums{1} Linear,
\tablenums{2} Exponential, \tablenums{3} Curve \\
\texttt{StemDirection} & \tablenums{0} Up, \tablenums{1} Down \\
\texttt{MeasureNumberVisibility} & \tablenums{0} Auto,
\tablenums{1} Always, \tablenums{2} Never \\
\texttt{Aleatoric\allowbreak AnchoringDiscipline} & \tablenums{0} Musical,
\tablenums{1} WallClock, \tablenums{2} EitherPerEvent,
\tablenums{3} FreelyMixed \\
\texttt{NoteValue} & \tablenums{0} Whole, \tablenums{1} Half,
\tablenums{2} Quarter, \tablenums{3} Eighth, \tablenums{4} Sixteenth,
\tablenums{5} ThirtySecond, \tablenums{6} SixtyFourth \\
\texttt{ClefShape} & \tablenums{0} G, \tablenums{1} F, \tablenums{2} C,
\tablenums{3} Percussion \\
\bottomrule
\end{longtable}
\endgroup
\subsection{Tagged unions (one byte, then the variant payload)}
\begingroup\small
\begin{longtable}{p{1.9in} p{3.7in}}
\toprule
\textbf{Type} & \textbf{Discriminants} \\
\midrule
\endhead
\texttt{AnchorOffset} & \tablenums{0} Musical, \tablenums{1} WallClock,
\tablenums{2} Zero \\
\texttt{TimeAnchor} & \tablenums{0} Event, \tablenums{1} Measure,
\tablenums{2} Region, \tablenums{3} WallClock
(Section~\ref{sec:values:representative}) \\
\texttt{EventPosition} & \tablenums{0} Musical, \tablenums{1} WallClock \\
\texttt{ConcreteDuration} & \tablenums{0} Musical, \tablenums{1} WallClock \\
\texttt{EventDuration} & \tablenums{0} Musical, \tablenums{1} WallClock,
\tablenums{2} Indeterminate \\
\texttt{TimeBounds} & \tablenums{0} MusicalRange,
\tablenums{1} WallClockRange, \tablenums{2} Unbounded \\
\texttt{PitchSpacePosition} & \tablenums{0} Cmn, \tablenums{1} Integer,
\tablenums{2} JiVector, \tablenums{3} Registered \\
\texttt{TuningReference} & \tablenums{0} Inherit, \tablenums{1} Explicit \\
\texttt{AcousticRealization} & \tablenums{0} Implicit,
\tablenums{1} CentsOffset, \tablenums{2} AbsoluteHz \\
\texttt{SpellingNominal} & \tablenums{0} Cmn, \tablenums{1} Integer,
\tablenums{2} Registered \\
\texttt{SpellingSource} & \tablenums{0} UserChosen, \tablenums{1} Inferred,
\tablenums{2} Imported, \tablenums{3} Propagated,
\tablenums{4} Analytical \\
\texttt{SpellingScope} & \tablenums{0} Pitch, \tablenums{1} Range \\
\texttt{SpellingDirective} & \tablenums{0} Explicit, \tablenums{1} Rule \\
\texttt{VoiceSelector} & \tablenums{0} All, \tablenums{1} Voices \\
\texttt{GraceKind} & \tablenums{0} Acciaccatura, \tablenums{1} Appoggiatura,
\tablenums{2} Unmeasured, \tablenums{3} MeasuredFraction \\
\texttt{IndeterminacyKind} & \tablenums{0} Pitch, \tablenums{1} Duration,
\tablenums{2} Choice, \tablenums{3} Compound \\
\texttt{TrajectoryEndpoint} & \tablenums{0} EventPitch,
\tablenums{1} ExplicitPitch \\
\texttt{TrajectoryShape} & \tablenums{0} Linear, \tablenums{1} Exponential,
\tablenums{2} Curve, \tablenums{3} Stepwise \\
\texttt{Event} & \tablenums{0} Pitched, \tablenums{1} Unpitched,
\tablenums{2} Rest, \tablenums{3} Indeterminate, \tablenums{4} Trajectory,
\tablenums{5} Graphic, \tablenums{6} Cue
(Section~\ref{sec:values:representative}) \\
\texttt{RegionTimeModel} & \tablenums{0} Metric, \tablenums{1} Proportional,
\tablenums{2} Aleatoric \\
\texttt{RegionContent} & \tablenums{0} StaffBased, \tablenums{1} FreeGraphic,
\tablenums{2} Hybrid \\
\texttt{VoiceOrigin} & \tablenums{0} UserDeclared, \tablenums{1} Imported,
\tablenums{2} SystemPromoted \\
\texttt{StaffGroupKind} & \tablenums{0} GrandStaff, \tablenums{1} Bracket,
\tablenums{2} SubBracket, \tablenums{3} Choral, \tablenums{4} Registered \\
\texttt{TieClass} & \tablenums{0} Standard, \tablenums{1} Editorial,
\tablenums{2} CrossVoice, \tablenums{3} LaissezVibrer,
\tablenums{4} Registered \\
\texttt{AnnotationAnchor} & \tablenums{0} Event, \tablenums{1} Range,
\tablenums{2} Region \\
\texttt{GestureAnchoring} & \tablenums{0} Events, \tablenums{1} Range,
\tablenums{2} Free \\
\texttt{DecompositionSource} & \tablenums{0} UserChosen,
\tablenums{1} Inferred, \tablenums{2} Imported, \tablenums{3} Propagated \\
\texttt{TimeSignatureDisplay} & \tablenums{0} Standard,
\tablenums{1} Compound, \tablenums{2} Irrational,
\tablenums{3} MixedDenominators, \tablenums{4} None,
\tablenums{5} Symbolic \\
\bottomrule
\end{longtable}
\endgroup
\begin{requirement}
\label{req:binfmt:spelling-source-order}
\textbf{The \texttt{SpellingSource} / \texttt{SpellingSourceKind} trap.}
These two vocabularies name the same five provenance concepts but assign
them \emph{different} discriminants: \texttt{SpellingSourceKind} (the
C-style precedence-list element) is \tablenums{0}~UserChosen,
\tablenums{1}~Imported, \tablenums{2}~Propagated, \tablenums{3}~Inferred,
\tablenums{4}~Analytical, while \texttt{SpellingSource} (the payload-carrying
attachment provenance) is \tablenums{0}~UserChosen, \tablenums{1}~Inferred,
\tablenums{2}~Imported, \tablenums{3}~Propagated, \tablenums{4}~Analytical.
The orders differ (\texttt{Inferred} is 3 in one and 1 in the other), both
are golden-locked, and an implementation \MUSTNOT{} share one discriminant
function between them.
\end{requirement}
\section{Representative Complete Layouts}
\label{sec:values:representative}
The following layouts are spelled byte-by-byte both as worked examples of the
composition rules and as normative fact. Recall that inside this chapter's
codec every leaf is framed (\texttt{u32} LE length \cat{} bytes), so an
embedded 16-byte identifier occupies $4+16=20$ bytes.
\subsection{\texttt{RationalTime}}
As a leaf inside a composite: \texttt{u32} LE length \cat{} the canonical
form of Section~\ref{sec:primitives:rationaltime} (sign \cat{}
length-prefixed big-endian numerator magnitude \cat{} length-prefixed
big-endian denominator magnitude).
\subsection{\texttt{TimeAnchor}}
One discriminant byte, then:
\begin{description}
\item[\tablenums{0} Event] \texttt{id} (\texttt{EventId} leaf, 20) \cat{}
\texttt{offset} (\texttt{AnchorOffset}: tag byte, then for Musical a
\texttt{MusicalDuration} leaf, for WallClock a
\texttt{WallClockDuration} leaf ($4+8=12$), for Zero nothing).
\item[\tablenums{1} Measure] \texttt{id} (\texttt{MeasureId} leaf, 20)
\cat{} \texttt{position} (\texttt{MeasurePosition}, 1) \cat{}
\texttt{offset} (\texttt{AnchorOffset}).
\item[\tablenums{2} Region] \texttt{id} (\texttt{RegionId} leaf, 20) \cat{}
\texttt{edge} (\texttt{RegionEdge}, 1) \cat{} \texttt{offset}
(\texttt{AnchorOffset}).
\item[\tablenums{3} WallClock] \texttt{time} (\texttt{WallClockTime} leaf,
12).
\end{description}
\subsection{\texttt{Pitch}}
Positional struct: \texttt{scale\_position} \cat{} \texttt{acoustic}.
\begin{itemize}
\item \texttt{ScalePosition} = \texttt{space} (\texttt{PitchSpaceId}: NFC
string, \texttt{u32} LE length \cat{} UTF-8) \cat{} \texttt{position}
(\texttt{PitchSpacePosition}: tag byte, then Cmn = \texttt{nominal}
(\texttt{CmnNominal}, 1) \cat{} \texttt{alteration} (\texttt{i8}, 1)
\cat{} \texttt{octave} (\texttt{i8}, 1); Integer =
\texttt{space\_size} (\texttt{u16} LE, 2) \cat{} \texttt{index}
(\texttt{i32} LE, 4); JiVector = \texttt{u32} LE count \cat{} that many
\texttt{i32} LE components; Registered = a catalog-id string).
\item \texttt{AcousticPitch} = \texttt{tuning} (\texttt{TuningReference}:
tag byte; Explicit carries a \texttt{TuningSystemId} catalog-id string)
\cat{} \texttt{realization} (\texttt{AcousticRealization}: tag byte;
CentsOffset and AbsoluteHz each carry a \texttt{CanonicalF64} leaf,
$4+8=12$).
\end{itemize}
\subsection{\texttt{Event}}
One discriminant byte (\tablenums{0}--\tablenums{6}), then the variant struct
positionally. All seven variants share the prefix \texttt{id}
(\texttt{EventId} leaf) \cat{} \texttt{voice} (\texttt{VoiceId} leaf) \cat{}
\texttt{position} (\texttt{EventPosition}) \cat{} \texttt{duration}
(\texttt{EventDuration}); the remaining fields, in order:
\begingroup\small
\begin{longtable}{p{0.35in} p{1.35in} p{3.8in}}
\toprule
\textbf{Disc} & \textbf{Variant} & \textbf{Fields after the common prefix} \\
\midrule
\endhead
\tablenums{0} & \texttt{Pitched} &
\texttt{pitches} \cat{} \texttt{articulations} \cat{} \texttt{dynamic}
\cat{} \texttt{ornaments} \cat{} \texttt{stem} \cat{} \texttt{grace} \\
\tablenums{1} & \texttt{Unpitched} &
\texttt{staff\_position} \cat{} \texttt{instrument\_member} \cat{}
\texttt{articulations} \cat{} \texttt{dynamic} \cat{} \texttt{stem}
\cat{} \texttt{grace} \\
\tablenums{2} & \texttt{Rest} &
\texttt{vertical\_position} \cat{} \texttt{visible} \\
\tablenums{3} & \texttt{Indeterminate} &
\texttt{indeterminacy} \cat{} \texttt{hints} \\
\tablenums{4} & \texttt{Trajectory} &
\texttt{start} \cat{} \texttt{end} \cat{} \texttt{shape} \cat{}
\texttt{display} \\
\tablenums{5} & \texttt{Graphic} &
\texttt{graphics} \cat{} \texttt{playback\_bindings} \\
\tablenums{6} & \texttt{Cue} &
\texttt{source} \cat{} \texttt{rendering} \\
\bottomrule
\end{longtable}
\endgroup
Each field encodes under this chapter's rules for its type (vectors are
\texttt{u32}-counted, options carry a presence byte, embedded identifiers are
leaves, unions carry their tag byte).
\subsection{\texttt{Slur}}
Positional struct of three leaves: \texttt{id} (\texttt{SlurId} leaf, 20)
\cat{} \texttt{start\_event} (\texttt{EventId} leaf, 20) \cat{}
\texttt{end\_event} (\texttt{EventId} leaf, 20) --- 60 bytes total.
\section{The \texttt{CanonicalValue} Seam}
\label{sec:values:canonical-value}
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
twenty-three types (the Phase-3 operation tranche added
\texttt{Staff}/\texttt{TimeSignature}/\texttt{TempoSegment}/%
\texttt{StaffLineConfiguration}; the repeat-authoring revision added
\texttt{RepeatStructure}):
\begin{quote}
\texttt{Event}, \texttt{Rest}, \texttt{Pitch}, \texttt{IdentifiedPitch},
\texttt{PitchSpelling}, \texttt{Tie}, \texttt{Slur}, \texttt{Beam},
\texttt{Spanner}, \texttt{RegionTimeModel}, \texttt{TimeAnchor},
\texttt{Region}, \texttt{StaffInstance}, \texttt{Voice},
\texttt{DecompositionAttachment}, \texttt{SpellingSourceKind},
\texttt{ScoreMetadata}, \texttt{MetricGrid}, \texttt{Staff},
\texttt{TimeSignature}, \texttt{TempoSegment},
\texttt{StaffLineConfiguration}, \texttt{RepeatStructure}.
\end{quote}
The seam introduces \textbf{no new bytes}: a value's stand-alone canonical
bytes are byte-for-byte the bytes the whole-\texttt{Score} codec embeds for
that value. Stand-alone decode applies the same validation and rejects
trailing bytes.
\section{The Frozen-Layout Rule}
\label{sec:values:frozen}
\begin{requirement}
\label{req:binfmt:frozen-layout}
\textbf{Frozen positional layouts (the schema-evolution keystone).} Within
schema major~0, every positional struct layout in this chapter is
\textbf{frozen}: there is no field-addition mechanism at the value level ---
no per-field framing, no optional-field encoding, no trailing-field
tolerance. Any change to any struct's field \emph{set} or field \emph{order}
is a schema-\textsc{major} change and requires a documented migration in this
document's revision history (Chapter~\ref{ch:evolution}).
Appending a \emph{new variant} to an \textbf{open} discriminant vocabulary is
a schema-\textsc{minor} change under the rules of
Chapter~\ref{ch:evolution}. At the value layer, the open vocabularies are
those with a \texttt{Registered} escape variant ---
\texttt{PitchSpacePosition}, \texttt{SpellingNominal},
\texttt{StaffGroupKind}, \texttt{TieClass} --- through which extensions
attach without any wire change at all; every \emph{other} union in
Section~\ref{sec:values:discriminants} (including \texttt{Event},
\texttt{TimeAnchor}, \texttt{TimeSignatureDisplay}) is closed in v0 and may
grow only by an append ratified in a revision of this document.
This rule ratifies the project's staging decision that data-model payload
expansion --- \texttt{SlurKind}, beam geometry, voltas, instrument bodies,
score-metadata growth, and their kin --- lands as a coordinated
schema-\textsc{major} revision of this document, not as ad-hoc field
insertion.
\end{requirement}
% ===========================================================================
\chapter{Operation Wire Forms}
\label{ch:ops}
This chapter pins the operation layer: envelopes, payloads, stamps, causal
contexts, effects, and materialized state. It \textbf{ratifies} the
encodings the ops crate's \texttt{DECISIONS} record held as ``provisional
canonical encoding'' (mirroring core P11-4); that provisional status is
discharged.
Regime (a) applies, with one composition difference from
Chapter~\ref{ch:values}: the operation layer embeds \emph{fixed-width}
canonical leaves (identifiers, hashes, \texttt{ConflictId}) \textbf{raw},
without a length prefix (\texttt{push\_canon}); only variable-width parts
carry \texttt{u32} LE length prefixes (\texttt{push\_lp\_bytes}). Sequences
(\texttt{push\_seq}) are a \texttt{u32} LE count, then each element
individually length-prefixed (\texttt{u32} LE), regardless of element width.
Text (\texttt{push\_str}) is NFC-normalized at encode time, then
length-prefixed UTF-8. Embedded graph values are the
\texttt{CanonicalValue} bytes of Section~\ref{sec:values:canonical-value},
framed by a \texttt{u32} LE length prefix (Operation Catalog requirement
\texttt{req:catalog:value-encoding}).
\section{The Operation Envelope}
\label{sec:ops:envelope}
\begin{requirement}
\label{req:binfmt:envelope}
An \texttt{OperationEnvelope}'s canonical bytes are, in order:
\begin{center}
\begin{tabular}{p{1.35in} p{3.9in}}
\toprule
\textbf{Field} & \textbf{Encoding} \\
\midrule
\texttt{id} & \texttt{OperationId}, 16 big-endian bytes, raw \\
\texttt{author} & \texttt{AuthorId}, 16 big-endian bytes, raw \\
\texttt{stamp} & \texttt{OperationStamp}, 28 bytes
(Section~\ref{sec:ops:stamp}) \\
\texttt{causal\_context} & \texttt{CausalContext}
(Section~\ref{sec:ops:causal}) \\
\texttt{transaction} & option tag byte (\tablenums{0} absent /
\tablenums{1} present), then \texttt{TransactionId} 16 BE if present \\
\texttt{payload} & \texttt{OperationPayload}
(Section~\ref{sec:ops:payload}) \\
\bottomrule
\end{tabular}
\end{center}
\textbf{Id-leads property.} The envelope's first 16 bytes \emph{are} the
\texttt{OperationId}'s canonical bytes. This is load-bearing: the operation
index (Chapter~\ref{ch:bundle}, Section~\ref{sec:bundle:opindex}) keys its
entries on those leading 16 bytes without decoding envelopes, and the
sanctioned partial read (\emph{peek}) of an envelope is exactly its leading
16 bytes. Any future envelope revision \MUST{} preserve id-leads or revise
the operation index in the same schema-major step.
\end{requirement}
$\texttt{EnvelopeHash} = \mathrm{BLAKE3}(\texttt{MUSCENVH} \cat
\mathit{envelope\ canonical\ bytes})$ --- the full 32-byte digest.
Envelopes are \textbf{encode-only} at this layer, deliberately: once
committed, an envelope is stored and transported as opaque bytes and is
never reconstructed into typed form by the storage layer; the peek above is
the only sanctioned partial read. (Reducers decode payloads they authored or
received through the ops layer's own typed surface; the wire contract is
that stored envelope bytes are preserved verbatim, since
\texttt{EnvelopeHash} commits to them.)
\section{\texttt{OperationPayload} and the Meta-Operations}
\label{sec:ops:payload}
\texttt{OperationPayload}: one discriminant byte, then the variant.
\begin{center}
\small
\begin{tabular}{p{0.35in} p{1.55in} p{3.3in}}
\toprule
\textbf{Disc} & \textbf{Variant} & \textbf{Payload} \\
\midrule
\tablenums{0} & \texttt{Primitive} & an \texttt{OperationKind}
(Section~\ref{sec:ops:kinds}) \\
\tablenums{1} & \texttt{ResolveConflict} & \texttt{target}
(\texttt{ConflictId}, 16 BE) \cat{} \texttt{action}
(\texttt{ResolutionAction}) \\
\tablenums{2} & \texttt{UndoTransaction} & \texttt{target}
(\texttt{TransactionId}, 16 BE) \cat{} \texttt{policy}
(\texttt{UndoPolicy}, 1 byte) \\
\tablenums{3} & \texttt{ResolveEquivocation} & \texttt{target}
(\texttt{OperationId}, 16 BE) \cat{} \texttt{chosen}
(\texttt{EnvelopeHash}, 32) \\
\bottomrule
\end{tabular}
\end{center}
Discriminants \tablenums{0}--\tablenums{2} are the ratified v1 values;
\tablenums{3} was appended by the catalog's \sectionsc{ResolveEquivocation
(meta-operation)} entry. The vocabulary is append-only: new meta-operations
take $\geq 4$.
\texttt{UndoPolicy} is one byte: \tablenums{0} StrictInverse,
\tablenums{1} BestEffort, \tablenums{2} Cascade.
\texttt{ResolutionAction} is one byte then an optional payload
(\texttt{app:bytes}, core requirement
\texttt{req:semops:\allowbreak resolution-action-discriminants}): \tablenums{0}
AcceptLoser, \tablenums{1} KeepWinner, \tablenums{2} Override
(\cat{} \texttt{OperationId} 16 BE), \tablenums{3} Reanchor (\cat{}
\texttt{TypedObjectId}), \tablenums{4} Dismiss, \tablenums{5} Registered
(\cat{} \texttt{ResolutionRegistryId} 16 BE).
\section{\texttt{OperationKind}: the Primitive Wire Discriminants}
\label{sec:ops:kinds}
\begin{requirement}
\label{req:binfmt:kind-discriminants}
The \texttt{OperationKind} wire discriminant is one byte, golden-locked to
the table below. The vocabulary is \textbf{append-only}: new primitive kinds
take discriminants past \tablenums{29}; the assignments below never change.
\end{requirement}
Each row also pins the payload's byte layout; the field \emph{set and order}
is the Operation Catalog's (the cited section governs semantics).
``$\mathrm{lp}(T)$'' means the \texttt{u32}-LE-length-prefixed
\texttt{CanonicalValue} bytes of a \texttt{T}
(Section~\ref{sec:values:canonical-value}); bare identifiers are raw 16
big-endian bytes; ``$\mathrm{seq}^{\uparrow}$'' is a \texttt{push\_seq}
sequence sorted ascending by canonical bytes, and
``$\mathrm{seq}^{\Uparrow}$'' is the same sequence constrained to be
\emph{strictly} increasing --- a set, not a multiset. A decoder \MUST{}
reject a $\mathrm{seq}^{\Uparrow}$ containing a duplicate rather than
normalize it away, since the duplicate is the only evidence that the writer
and the reader disagree about the field's type.
\begingroup\footnotesize
\begin{longtable}{p{0.32in} >{\raggedright\arraybackslash}p{1.5in} >{\raggedright\arraybackslash}p{2.35in} >{\raggedright\arraybackslash}p{1.35in}}
\toprule
\textbf{Disc} & \textbf{Kind} & \textbf{Payload layout} &
\textbf{Catalog section} \\
\midrule
\endhead
\tablenums{0} & \texttt{InsertEvent} &
\texttt{staff\_instance} (16) \cat{} $\mathrm{lp}$(\texttt{Event}) &
\sectionsc{InsertEvent} \\
\tablenums{1} & \texttt{DeleteEvent} &
\texttt{event} (16) \cat{} \texttt{TupletCompensation} &
\sectionsc{DeleteEvent} \\
\tablenums{2} & \texttt{RespellPitch} &
\texttt{pitch} (16) \cat{} $\mathrm{lp}$(\texttt{PitchSpelling}) &
\sectionsc{RespellPitch} \\
\tablenums{3} & \texttt{CreateCrossCutting} &
\texttt{CrossCuttingValue} &
\sectionsc{CreateCrossCutting} \\
\tablenums{4} & \texttt{ChangeRegionTimeModel} &
\texttt{region} (16) \cat{} $\mathrm{lp}$(\texttt{RegionTimeModel})
\cat{} $\mathrm{seq}^{\uparrow}$(\texttt{EventId}) \cat{}
\texttt{PositionRemapping} &
\sectionsc{ChangeRegion\allowbreak TimeModel} \\
\tablenums{5} & \texttt{SetUserSystemBreak} &
\texttt{region} (16) \cat{} $\mathrm{lp}$(\texttt{TimeAnchor}) \cat{}
\texttt{present} (bool, 1) &
\sectionsc{SetUserSystemBreak} \\
\tablenums{6} & \texttt{DeclareTransaction} &
\texttt{id} (16) \cat{} \texttt{label} (NFC string) \cat{} option tag
\cat{} \texttt{TransactionCategory} if present &
\sectionsc{DeclareTransaction} \\
\tablenums{7} & \texttt{Registered} &
\texttt{OperationKindRegistryId} (16) \cat{}
$\mathrm{lp}$(opaque bytes) &
\sectionsc{K1 --- Framework Slots} \\
\tablenums{8} & \texttt{ModifyEvent} &
$\mathrm{lp}$(\texttt{Event}) &
\sectionsc{ModifyEvent} \\
\tablenums{9} & \texttt{Transpose} &
$\mathrm{seq}^{\uparrow}$(\texttt{PitchId}) \cat{}
\texttt{chromatic\_steps} (\texttt{i32} LE, 4). Frozen: the sequence is a
multiset, \emph{not} $\mathrm{seq}^{\Uparrow}$ &
\sectionsc{Transpose} \\
\tablenums{10} & \texttt{InsertIdentifiedPitch} &
\texttt{event} (16) \cat{} $\mathrm{lp}$(\texttt{IdentifiedPitch}) &
\sectionsc{Identified-Pitch Operations} \\
\tablenums{11} & \texttt{DeleteIdentifiedPitch} &
\texttt{pitch} (16) &
\sectionsc{Identified-Pitch Operations} \\
\tablenums{12} & \texttt{ModifyIdentifiedPitch} &
\texttt{pitch} (16) \cat{} $\mathrm{lp}$(\texttt{Pitch}) &
\sectionsc{Identified-Pitch Operations} \\
\tablenums{13} & \texttt{DeleteCrossCutting} &
\texttt{structure} (\texttt{TypedObjectId}, 18/34) &
\sectionsc{DeleteCrossCutting} \\
\tablenums{14} & \texttt{ModifyCrossCutting} &
\texttt{CrossCuttingValue} &
\sectionsc{ModifyCrossCutting} \\
\tablenums{15} & \texttt{CreateRegion} &
$\mathrm{lp}$(\texttt{Region}) &
\sectionsc{Structural Containers} \\
\tablenums{16} & \texttt{DeleteRegion} &
\texttt{region} (16) &
\sectionsc{Structural Containers} \\
\tablenums{17} & \texttt{CreateStaffInstance} &
\texttt{region} (16) \cat{} $\mathrm{lp}$(\texttt{StaffInstance}) &
\sectionsc{Structural Containers} \\
\tablenums{18} & \texttt{DeleteStaffInstance} &
\texttt{staff\_instance} (16) &
\sectionsc{Structural Containers} \\
\tablenums{19} & \texttt{CreateVoice} &
\texttt{staff\_instance} (16) \cat{} $\mathrm{lp}$(\texttt{Voice}) &
\sectionsc{Structural Containers} \\
\tablenums{20} & \texttt{DeleteVoice} &
\texttt{voice} (16) &
\sectionsc{Structural Containers} \\
\tablenums{21} & \texttt{SetMetadata} &
$\mathrm{lp}$(\texttt{ScoreMetadata}) &
\sectionsc{Score Settings} \\
\tablenums{22} & \texttt{SetMetricGrid} &
\texttt{region} (16) \cat{} option tag (\tablenums{0}/\tablenums{1})
\cat{} $\mathrm{lp}$(\texttt{MetricGrid}) if present &
\sectionsc{Score Settings} \\
\tablenums{23} & \texttt{SetUserPageBreak} &
\texttt{region} (16) \cat{} $\mathrm{lp}$(\texttt{TimeAnchor}) \cat{}
\texttt{present} (bool, 1) &
\sectionsc{Score Settings} \\
\tablenums{24} & \texttt{CreateStaff} &
$\mathrm{lp}$(\texttt{Staff}) &
\sectionsc{CreateStaff} \\
\tablenums{25} & \texttt{SetTimeSignature} &
\texttt{region} (16) \cat{} $\mathrm{lp}$(\texttt{TimeAnchor}) \cat{}
option tag (\tablenums{0}/\tablenums{1}) \cat{}
$\mathrm{lp}$(\texttt{TimeSignature}) if present &
\sectionsc{Meter and Tempo Overwrites} \\
\tablenums{26} & \texttt{SetTempoSegment} &
option tag \cat{} \texttt{region} (16) if present \cat{}
$\mathrm{lp}$(\texttt{TimeAnchor}) \cat{} option tag \cat{}
$\mathrm{lp}$(\texttt{TempoSegment}) if present &
\sectionsc{Meter and Tempo Overwrites} \\
\tablenums{27} & \texttt{SetStaffLayout} &
\texttt{staff\_instance} (16) \cat{} option tag \cat{}
\texttt{instrument\_override} (16) if present \cat{} option tag \cat{}
$\mathrm{lp}$(\texttt{StaffLineConfiguration}) if present \cat{}
\texttt{visible} (bool, 1) &
\sectionsc{SetStaffLayout} \\
\tablenums{28} & \texttt{CreateRepeatStructure} &
$\mathrm{lp}$(\texttt{RepeatStructure}) --- the schema-major-2
layout, unconditionally (born at v2;
Section~\ref{sec:evolution:major2}) &
\sectionsc{Repeat Structures} \\
\tablenums{29} & \texttt{DeleteRepeatStructure} &
\texttt{repeat} (16) &
\sectionsc{Repeat Structures} \\
\tablenums{30} & \texttt{TransposeInterval} &
$\mathrm{seq}^{\Uparrow}$(\texttt{PitchId}) \cat{}
\texttt{diatonic\_steps} (\texttt{i32} LE, 4) \cat{}
\texttt{chromatic\_steps} (\texttt{i32} LE, 4) &
\sectionsc{TransposeInterval} \\
\bottomrule
\end{longtable}
\endgroup
Sub-vocabularies used above (one discriminant byte, then the listed
payload):
\begin{description}
\item[\texttt{TupletCompensation}.] \tablenums{0} NotInTuplet (empty);
\tablenums{1} ReplaceWithRest \cat{} $\mathrm{lp}$(\texttt{Rest});
\tablenums{2} RewriteTuplets \cat{}
$\mathrm{seq}^{\uparrow}$(\texttt{TupletId});
\tablenums{3} CascadeDeleteTuplets \cat{}
$\mathrm{seq}^{\uparrow}$(\texttt{TupletId}).
\item[\texttt{CrossCuttingValue}.] \tablenums{0} Tie, \tablenums{1} Slur,
\tablenums{2} Beam, \tablenums{3} Spanner; in every case the tag is
followed by $\mathrm{lp}$(the typed structure's \texttt{CanonicalValue}
bytes).
\item[\texttt{PositionRemapping}.] \tablenums{0} PreserveTime (empty);
\tablenums{1} Reassign \cat{} \texttt{u32} LE entry count \cat{}
entries, each \texttt{EventId} (16, raw) \cat{}
$\mathrm{lp}$(\texttt{MusicalPosition}), ascending by \texttt{EventId}.
\item[\texttt{TransactionCategory}.] \tablenums{0} NoteEntry,
\tablenums{1} Structural, \tablenums{2} Layout, \tablenums{3} Import,
\tablenums{4} Registered \cat{} \texttt{OperationKindRegistryId} (16 BE)
--- governed by \texttt{app:bytes} (core requirement
\texttt{req:semops:transaction-category}).
\end{description}
\texttt{OperationKind} is encode-only at the storage boundary, like the
envelope that carries it.
\section{\texttt{OperationKindTag}: a Separate Discriminant Space}
\label{sec:ops:kind-tag}
\begin{requirement}
\label{req:binfmt:kind-tag}
\texttt{OperationKindTag} --- the payload-free projection of an operation
kind, stored by edit barriers
(Chapter~\ref{ch:barriers}) --- has its \textbf{own} discriminant space,
assigned independently of Section~\ref{sec:ops:kinds}, with both encode and
validating decode. One byte; only \texttt{Registered} carries a payload (its
\texttt{OperationKindRegistryId}, 16 big-endian bytes, total 17). Unknown
discriminants and wrong lengths are decode errors. Append-only past
\tablenums{29}.
\end{requirement}
\begingroup\small
\begin{longtable}{p{0.5in} p{2.0in} p{0.5in} p{2.0in}}
\toprule
\textbf{Disc} & \textbf{Tag} & \textbf{Disc} & \textbf{Tag} \\
\midrule
\endhead
\tablenums{0} & \texttt{InsertEvent} & \tablenums{12} & \texttt{DeleteStaffInstance} \\
\tablenums{1} & \texttt{DeleteEvent} & \tablenums{13} & \texttt{SetUserSystemBreak} \\
\tablenums{2} & \texttt{ModifyEvent} & \tablenums{14} & \texttt{SetUserPageBreak} \\
\tablenums{3} & \texttt{RespellPitch} & \tablenums{15} & \texttt{DeclareTransaction} \\
\tablenums{4} & \texttt{Transpose} & \tablenums{16} & \texttt{Registered} \\
\tablenums{5} & \texttt{CreateCrossCutting} & \tablenums{17} & \texttt{InsertIdentifiedPitch} \\
\tablenums{6} & \texttt{DeleteCrossCutting} & \tablenums{18} & \texttt{DeleteIdentifiedPitch} \\
\tablenums{7} & \texttt{ModifyCrossCutting} & \tablenums{19} & \texttt{ModifyIdentifiedPitch} \\
\tablenums{8} & \texttt{ChangeRegionTimeModel} & \tablenums{20} & \texttt{CreateVoice} \\
\tablenums{9} & \texttt{InsertRegion} & \tablenums{21} & \texttt{DeleteVoice} \\
\tablenums{10} & \texttt{DeleteRegion} & \tablenums{22} & \texttt{SetMetadata} \\
\tablenums{11} & \texttt{InsertStaffInstance} & \tablenums{23} & \texttt{SetMetricGrid} \\
\tablenums{24} & \texttt{InsertStaff} & \tablenums{25} & \texttt{SetTimeSignature} \\
\tablenums{26} & \texttt{SetTempoSegment} & \tablenums{27} & \texttt{SetStaffLayout} \\
\tablenums{28} & \texttt{CreateRepeatStructure} & \tablenums{29} & \texttt{DeleteRepeatStructure} \\
\tablenums{30} & \texttt{TransposeInterval} & & \\
\bottomrule
\end{longtable}
\endgroup
\textbf{Naming mapping.} The kind-to-tag projection renames three pairs:
\texttt{OperationKind::\allowbreak CreateRegion} projects to
\texttt{OperationKindTag::\allowbreak InsertRegion}, and
\texttt{OperationKind::\allowbreak CreateStaffInstance} projects to
\texttt{OperationKindTag::\allowbreak InsertStaffInstance}, and
\texttt{OperationKind::\allowbreak CreateStaff} projects to
\texttt{OperationKindTag::\allowbreak InsertStaff}. The mismatch is
intentional
(the tag space predates the M2c naming) and is pinned here so barrier
authors target the right tag. The repeat-structure pair projects
name-verbatim (\texttt{CreateRepeatStructure}/%
\texttt{DeleteRepeatStructure}), as the cross-cutting tags do.
\section{Stamps and Causal Contexts}
\label{sec:ops:stamp}
\texttt{HybridLogicalClock}: \texttt{physical\_time} (\texttt{WallClockTime},
\texttt{i64} LE, 8) \cat{} \texttt{logical\_counter} (\texttt{u32} LE, 4).
\texttt{OperationStamp}: \texttt{hlc} (12) \cat{} \texttt{id}
(\texttt{OperationId}, 16 BE) --- \textbf{28 bytes} total, fixed width. The
canonical reduction tuple orders stamps as $(\mathit{physical\_time},
\mathit{logical\_counter}, \mathit{replica}, \mathit{counter})$, ascending;
the replica comparison equals big-endian byte order.
\label{sec:ops:causal}
\texttt{CausalContext} (dotted version vector):
\begin{center}
\begin{tabular}{p{0.7in} p{4.55in}}
\toprule
\textbf{Field} & \textbf{Encoding} \\
\midrule
vector & \texttt{u32} LE entry count \cat{} entries, each
\texttt{replica} (8 BE) \cat{} \texttt{counter} (\texttt{u64} LE, 8),
ascending by replica \\
dots & \texttt{u32} LE count \cat{} \texttt{OperationId}s (16 BE each),
ascending \\
\bottomrule
\end{tabular}
\end{center}
The vector is \emph{zero-based-floor} (P11-C7): an entry $(r, c)$ covers
counters $0..=c$ of replica $r$; absence of a replica covers nothing.
\section{Effects, Conflicts, Anomalies, and Materialized State}
\label{sec:ops:effects}
These types are canonical \emph{materialized} facts: they appear in
\texttt{MaterializedState}'s canonical bytes, so their discriminants and
layouts are normative wire facts even though they are never stored inside
envelopes. All decode validation is performed by the ops layer's validating
decoder; unknown discriminants, bad lengths, non-canonical orders, and
trailing bytes are decode errors.
\begingroup\footnotesize
\begin{longtable}{>{\raggedright\arraybackslash}p{1.6in} p{4.1in}}
\toprule
\textbf{Type} & \textbf{Layout (tag byte, then payload)} \\
\midrule
\endhead
\texttt{OperationEffect} &
\tablenums{0} Applied (empty);
\tablenums{1} AppliedWithRepair \cat{} seq(\texttt{RepairRecord});
\tablenums{2} Conflicted \cat{} \texttt{ConflictId} (16 BE);
\tablenums{3} TombstonedTarget \cat{} \texttt{TypedObjectId};
\tablenums{4} NoOp \cat{} \texttt{NoOpReason}. \\
\texttt{NoOpReason} &
\tablenums{0} TargetTombstoned; \tablenums{1} AlreadyApplied;
\tablenums{2} SupersededByLaterOperation \cat{} \texttt{OperationId}
(16 BE); \tablenums{3} PreconditionFailedUnderReduction \cat{}
\texttt{PreconditionFailureReason}; \tablenums{4} TransactionConflict. \\
\texttt{Precondition\allowbreak FailureReason} &
\tablenums{0} TargetMissing; \tablenums{1} TargetTombstoned;
\tablenums{2} WrongRegionTimeModel;
\tablenums{3} TupletCompensationInvalid;
\tablenums{4} EventDurationInvalid; \tablenums{5} PositionOutsideRegion;
\tablenums{6} PitchSpaceMismatch; \tablenums{7} VoiceMissing;
\tablenums{8} ExtensionPrecondition \cat{} id (16 BE);
\tablenums{9} Registered \cat{} id (16 BE);
\tablenums{10} ContainerNotEmpty;
\tablenums{11} TempoMapMalformed;
\tablenums{12} SystemDerivedContentImmutable (Pass~12, P12-K3);
\tablenums{13} RecreateContentMismatch (Pass~12, P12-K9). \\
\texttt{RepairRecord} &
(struct) \texttt{kind} (\texttt{RepairKind}) \cat{} \texttt{target}
(\texttt{TypedObjectId}). \\
\texttt{RepairKind} &
\tablenums{0} Reanchored \cat{} \texttt{from} (\texttt{TypedObjectId})
\cat{} \texttt{to} (\texttt{TypedObjectId}) \cat{}
\texttt{ReanchorReason};
\tablenums{1} SpannerTruncated \cat{} seq(\texttt{TypedObjectId});
\tablenums{2} Orphaned; \tablenums{3} CascadeDeleted;
\tablenums{4} AttachmentTombstoned;
\tablenums{5} VoicePromoted \cat{} \texttt{from} (\texttt{VoiceId},
16 BE) \cat{} \texttt{to} (\texttt{VoiceId}, 16 BE);
\tablenums{6} TupletCompensated \cat{} \texttt{TupletCompensationKind};
\tablenums{7} Registered \cat{} id (16 BE). \\
\texttt{ReanchorReason} &
\tablenums{0} SameVoiceNearer; \tablenums{1} SameStaffInstanceNearer;
\tablenums{2} SameStaffNearer; \tablenums{3} SameRegionNearer;
\tablenums{4} ExplicitFallback;
\tablenums{5} DeclaredByExtension \cat{} id (16 BE);
\tablenums{6} SameCanvasNearer (Pass~12, P12-C4: semantically the
rank-4 proximity variant; appended at 6 because 5 was already
owned by DeclaredByExtension). \\
\texttt{TupletCompensationKind} &
\tablenums{0} ReplaceWithRest; \tablenums{1} RewriteTuplets;
\tablenums{2} CascadeDeleteTuplets. (No payload; distinct from the
payload-carrying \texttt{TupletCompensation} of
Section~\ref{sec:ops:kinds}.) \\
\texttt{PendingReason} &
tag \cat{} blocker (\texttt{OperationId}, 16 BE) in every variant:
\tablenums{0} MissingCausalPredecessor; \tablenums{1}
DependsOnEquivocated; \tablenums{2} DependsOnExcluded;
\tablenums{3} DependsOnPending; \tablenums{4} HaltedBySystemCollision. \\
\texttt{ConflictKind} &
\tablenums{0} StructuralFieldCollision \cat{} \texttt{winner} (16 BE)
\cat{} \texttt{loser} (16 BE) \cat{} \texttt{FieldPath} (NFC string);
\tablenums{1} TransactionConflict \cat{} \texttt{transaction} (16 BE)
\cat{} $\mathrm{seq}^{\uparrow}$(\texttt{OperationId});
\tablenums{2} TombstonedTarget \cat{} \texttt{TypedObjectId} \cat{}
\texttt{OperationId} (16 BE);
\tablenums{3} ReanchorFailure \cat{} \texttt{original\_referent}
(\texttt{TypedObjectId}) \cat{} \texttt{referencing\_object}
(\texttt{TypedObjectId});
\tablenums{4} TimeModelMigrationFailure \cat{} \texttt{region} (16 BE)
\cat{} $\mathrm{seq}^{\uparrow}$(\texttt{TypedObjectId});
\tablenums{5} ExtensionConflict \cat{} \texttt{kind\_id} (16 BE) \cat{}
$\mathrm{lp}$(opaque details). \\
\texttt{ConflictResolutionState} &
\tablenums{0} Unresolved; \tablenums{1} Resolved \cat{} \texttt{by}
(16 BE) \cat{} \texttt{ResolutionAction}; \tablenums{2} Dismissed \cat{}
\texttt{by} (16 BE). \\
\texttt{ConflictRecord} &
(struct) \texttt{id} (\texttt{ConflictId}, 16 BE) \cat{}
seq(\texttt{caused\_by}: \texttt{OperationId}, stored sorted) \cat{}
\texttt{kind} \cat{} seq(\texttt{affected\_objects}:
\texttt{TypedObjectId}, stored sorted) \cat{}
\texttt{resolution\_state}. \\
\texttt{IntegrityAnomaly} &
(struct) \texttt{id} (\texttt{IntegrityAnomalyId}, 16 BE) \cat{}
\texttt{kind}. \\
\texttt{IntegrityAnomalyKind} &
\tablenums{0} SystemIdentifierCollision \cat{} \texttt{ObjectKind}
\cat{} \texttt{colliding\_counter} (\texttt{u64} LE) \cat{} the two
input-set blobs, each $\mathrm{lp}$(bytes), emitted lo${}\leq{}$hi by
byte comparison;
\tablenums{1} OperationSlotEquivocated \cat{} \texttt{OperationId}
(16 BE);
\tablenums{2} ReplicaStreamQuarantined \cat{} \texttt{replica} (8 BE)
\cat{} \texttt{first\_bad\_counter} (\texttt{u64} LE);
\tablenums{3} Registered \cat{} id (16 BE). \\
\texttt{AnomalousReplicaSegment} &
(struct) \texttt{replica} (8 BE) \cat{} \texttt{first\_bad\_counter}
(\texttt{u64} LE) \cat{} \texttt{ReplicaAnomalyReason}
(\tablenums{0} HlcMonotonicityViolation \cat{} two
\texttt{OperationId}s; \tablenums{1} Registered \cat{} id 16 BE)
\cat{} seq(\texttt{excluded}: \texttt{OperationId}, ascending counter). \\
\texttt{FieldPath} &
an NFC string (\texttt{u32} LE length \cat{} UTF-8). \\
\texttt{ObjectState} &
\tablenums{0} Live (empty); \tablenums{1} Tombstoned \cat{}
\texttt{deleted\_by} (\texttt{OperationId}, 16 BE) \cat{}
\texttt{minted\_by} (\texttt{OperationId}, 16 BE). \\
\texttt{ObjectKind} (ops) &
\tablenums{0} Voice; \tablenums{1} Pitch; \tablenums{2} Registered
\cat{} \texttt{OperationKindRegistryId} (16 BE) --- governed by
\texttt{app:bytes} (core requirement
\texttt{req:graph:object-kind-vocab}). Distinct from the barrier layer's
2-byte \texttt{ObjectKind} (Chapter~\ref{ch:barriers}). \\
\bottomrule
\end{longtable}
\endgroup
\subsection{\texttt{MaterializedState}}
\label{sec:ops:materialized}
The canonical bytes of a reduction's materialized state are eight sections,
concatenated; every section is \texttt{u32}-LE-counted (the conflict
registry carries its own count) and in the stated normative order, so the
bytes are identical across any permutation of the input operation set:
\begin{enumerate}
\item \textbf{effects} --- count, then per entry \texttt{OperationId}
(16 BE, raw) \cat{} $\mathrm{lp}$(\texttt{OperationEffect}); in
canonical reduction order;
\item \textbf{conflict registry} --- a seq of
$\mathrm{lp}$(\texttt{ConflictRecord}), ascending \texttt{ConflictId};
\item \textbf{anomalies} --- count, then each
$\mathrm{lp}$(\texttt{IntegrityAnomaly}), ascending
\texttt{IntegrityAnomalyId};
\item \textbf{objects} --- count, then per entry \texttt{TypedObjectId}
(raw) \cat{} \texttt{ObjectState}, ascending \texttt{TypedObjectId};
\item \textbf{spellings} --- count, then per entry \texttt{PitchId}
(16 BE, raw) \cat{} $\mathrm{lp}$(\texttt{PitchSpelling}), ascending
\texttt{PitchId};
\item \textbf{breaks} --- count, then per entry \texttt{RegionId}
(16 BE, raw) \cat{} \texttt{MusicalPosition} (raw \texttt{RationalTime}
form, self-describing, \emph{not} leaf-framed) \cat{} \texttt{present}
(bool, 1); ascending $(\mathit{region}, \mathit{position})$;
\item \textbf{page breaks} --- same shape and order as breaks;
\item \textbf{pending} --- count, then per entry \texttt{OperationId}
(16 BE, raw) \cat{} \texttt{PendingReason}, ascending
\texttt{OperationId}.
\end{enumerate}
Decode is fully validating (orders, tags, lengths, trailing bytes) and
rejects non-canonical input.
\section{v0 Payloads Have No Wire Form}
\label{sec:ops:v0}
The prototype's v0 identifier-projection payload types are an
\textbf{in-memory migration guard only}: they carry no canonical encoding,
and no v0 wire form was ever shipped. The v0${\to}$v1 migration is
\emph{semantic} --- typed value reconstruction, specified in the Operation
Catalog's \sectionsc{v0 $\rightarrow$ v1 Payload Migration} chapter --- not a
byte-level translation, and nothing in this document defines bytes for a v0
payload.
% ===========================================================================
\chapter{Bundle Physical Layout}
\label{ch:bundle}
The physical file: the fixed prelude, chunks, blocks, the manifest, and the
operation index. This chapter \textbf{ratifies} the bundle crate's
provisional encodings (its \texttt{DECISIONS} entries P11-D2, P11-D4, and
P11-D5 pending-companion status is discharged) and the operation-index
byte form (P12-D1). Structural \emph{semantics} --- superblock selection,
commit protocol, recovery, retention --- are core specification Chapter~8;
this chapter pins bytes only. Regime (a) applies except where a table says
otherwise.
\section{The Fixed Header (64 bytes)}
\label{sec:bundle:header}
Offset 0 of every bundle. Written at creation, never rewritten within a
format major version. All integers little-endian.
\begingroup\small
\begin{longtable}{p{0.8in} p{1.7in} p{3.1in}}
\toprule
\textbf{Range} & \textbf{Field} & \textbf{Value / rule} \\
\midrule
\endhead
\tablenums{0..8} & magic & \texttt{MUSCBND\textbackslash0} (8 bytes, ASCII,
trailing NUL) \\
\tablenums{8..10} & \texttt{format\_major} (\texttt{u16}) & \tablenums{0}
in this version; readers reject other values \\
\tablenums{10..12} & \texttt{format\_minor} (\texttt{u16}) & \tablenums{1}
written in this version \\
\tablenums{12..16} & \texttt{header\_length} (\texttt{u32}) &
\tablenums{64}; readers reject other values \\
\tablenums{16..24} & \texttt{superblock\_a\_offset} (\texttt{u64}) &
\tablenums{64}; readers reject other values \\
\tablenums{24..32} & \texttt{superblock\_b\_offset} (\texttt{u64}) &
\tablenums{320}; readers reject other values \\
\tablenums{32..48} & \texttt{file\_uuid} & 16 opaque bytes \\
\tablenums{48..60} & reserved & written zero; ignored on read \\
\tablenums{60..64} & \texttt{header\_crc} & CRC-32C of bytes
\tablenums{0..60}, \texttt{u32} LE \\
\bottomrule
\end{longtable}
\endgroup
Readers \MUST{} verify the magic and the CRC before consulting any other part
of the file.
\section{The Superblock Slots (256 bytes each)}
\label{sec:bundle:superblock}
Two slots, at offsets 64 (A) and 320 (B); the only mutable on-disk objects.
Selection \emph{semantics} (highest valid committed generation, the
equal-generation rule, torn-write fallback) are core Chapter~8,
\sectionsc{Superblock Selection}; the bytes:
\begingroup\small
\begin{longtable}{p{0.9in} p{2.15in} p{2.55in}}
\toprule
\textbf{Range} & \textbf{Field} & \textbf{Encoding} \\
\midrule
\endhead
\tablenums{0..8} & magic & \texttt{MUSCSUPR} (8 ASCII bytes) \\
\tablenums{8..16} & \texttt{generation} & \texttt{u64} LE \\
\tablenums{16..24} & \texttt{manifest\_offset} & \texttt{u64} LE \\
\tablenums{24..32} & \texttt{manifest\_length} & \texttt{u64} LE (also the
uncompressed length; the manifest is mandatorily uncompressed) \\
\tablenums{32..64} & \texttt{manifest\_hash} & 32 raw digest bytes \\
\tablenums{64..68} & \texttt{manifest\_schema\_version} &
\texttt{SchemaVersion} (\texttt{u16} LE \cat{} \texttt{u16} LE) \\
\tablenums{68..72} & \texttt{reduction\_algorithm\_version} &
\texttt{u32} LE \\
\tablenums{72..92} & \texttt{profile\_id} & 20 bytes: \texttt{u32} LE
discriminant (\tablenums{0} Full, \tablenums{1} ReadOnly,
\tablenums{2} Lite, \tablenums{3} Custom) \cat{}
\texttt{ProfileRegistryId} (16 raw bytes; writers \MUST{} emit zero
unless Custom, readers ignore it unless Custom). Core requirement
\texttt{req:format:profileid-discriminants}. \\
\tablenums{92..100} & \texttt{commit\_state} & two \texttt{u32} LE words:
Committed $= (\tablenums{0}, \tablenums{0})$; any tag
${}\neq \tablenums{0}$ decodes as Reserved(value) and the slot is
invalid for ordinary selection. Writers in this version \MUST{} produce
only Committed. \\
\tablenums{100..108} & \texttt{commit\_timestamp} & \texttt{i64} LE
nanoseconds; advisory --- selection never consults it \\
\tablenums{108..252} & reserved & written zero; ignored on read \\
\tablenums{252..256} & \texttt{superblock\_crc} & CRC-32C of bytes
\tablenums{0..252}, \texttt{u32} LE \\
\bottomrule
\end{longtable}
\endgroup
\section{Chunks}
\label{sec:bundle:chunks}
The bundle body begins at
$\texttt{BODY\_START} = 320 + 256 = \tablenums{576}$. A chunk's on-disk form
is its raw (possibly compressed) payload bytes at a byte offset ---
\textbf{no per-chunk length prefix, magic, or trailer on disk}; the offset,
lengths, compression, and hash live in the referencing \texttt{ChunkRef}. A
chunk reference whose offset is below \texttt{BODY\_START} is malformed.
\textbf{Content hash preimage} (core Chapter~8, \sectionsc{Domain-Separated
Preimages}; governs identity):
\[
\mathrm{BLAKE3}\bigl(\mathit{domain} \cat \mathit{kind}\,(1) \cat
\mathit{schema}\,(4) \cat \mathit{uncompressed\_length}\,
(\texttt{u64}\ \mathrm{LE}) \cat \mathit{payload}\bigr)
\]
where $\mathit{domain}$ is \texttt{MUSCMANI} for \texttt{Manifest} chunks and
\texttt{MUSCCHNK} otherwise. Compression is \emph{not} in the preimage.
Blob chunks are addressed by the bare \texttt{BlobId} form instead
(Chapter~\ref{ch:primitives}).
\textbf{\texttt{ChunkKind}} is a single byte, a \textbf{closed} vocabulary
(no \texttt{Registered} variant; core requirement
\texttt{req:format:chunkkind-discriminants}): \tablenums{0}
OperationEnvelopeBlock, \tablenums{1} OperationIndex, \tablenums{2}
Snapshot, \tablenums{3} Blob, \tablenums{4} ExtensionData, \tablenums{5}
TextProjection, \tablenums{6} LayoutCache, \tablenums{7} IntegrityIndex,
\tablenums{8} Manifest.
\textbf{\texttt{CompressionAlgorithm}} is a fixed two bytes, discriminant
\cat{} parameter: None $= (\tablenums{0},\tablenums{0})$;
Zstd$\{$level$\}$ $= (\tablenums{1}, \mathit{level})$; Reserved$(v)$ $=
(\tablenums{2}, v)$. \texttt{None} is \emph{not} a bare tag --- its zero
parameter byte is always present.
\begin{requirement}
\label{req:binfmt:compression-none-parameter}
A decoder \MUST{} reject a \texttt{CompressionAlgorithm} whose discriminant
is \tablenums{0} (\texttt{None}) and whose parameter byte is non-zero. It
\MUSTNOT{} ignore the byte, and \MUSTNOT{} normalize it to zero.
\end{requirement}
\begin{rationale}
Ignoring it made the codec \emph{lenient}, and therefore non-injective:
$(\tablenums{0}, \tablenums{255})$ and $(\tablenums{0}, \tablenums{0})$
decoded to one value, which re-encodes to the latter. Every structure
embedding a \texttt{ChunkRef} inherited that. A whole-value
re-encode-and-compare guard hides it --- the manifest has one --- but the
operation index does not, and so accepted two distinct payloads for one index
while its own contract promised to reject non-canonical bytes rather than
normalize them. Content-addressing rests on distinct bytes meaning distinct
values.
No conforming writer emits a non-zero parameter for \texttt{None}, so this
rejects only corrupt or adversarial input; no existing file changes meaning.
Found by the wire-decode fuzzer (Push~5, P3); supersedes the core
specification's earlier ``present but zero, and ignored on read''.
\end{rationale}
\textbf{Read rules.} Writers in this format version emit \texttt{None} only;
\emph{reading} zstd at any level is a conformance \MUST{}. A zstd payload is
decompressed into a buffer sized \emph{exactly} by the declared
\texttt{uncompressed\_length} (checked against the reader's resource policy
before allocation): a stream that would exceed the declaration, ends short of
it, or carries trailing garbage is corruption. After decompression the
content hash is verified over the uncompressed bytes, and
$\mathit{id} = \mathit{hash}$ is checked. A chunk whose declared schema
major differs from the reader's supported major is rejected
(Chapter~\ref{ch:evolution}). \texttt{Reserved} compression is rejected.
The \textbf{manifest chunk is mandatorily uncompressed}: a compressed
manifest reference is rejected before any bytes are read. The reference
reader bounds any single chunk at 256\,MiB
(\texttt{MAX\_CHUNK\_BYTES}); the bound is reader policy, not a wire fact.
\subsection{\texttt{ChunkRef} (95 bytes)}
\label{sec:bundle:chunkref}
\begingroup\small
\begin{longtable}{p{0.9in} p{1.9in} p{2.8in}}
\toprule
\textbf{Range} & \textbf{Field} & \textbf{Encoding} \\
\midrule
\endhead
\tablenums{0..32} & \texttt{id} & 32 raw digest bytes \\
\tablenums{32..33} & \texttt{kind} & \texttt{ChunkKind} byte \\
\tablenums{33..37} & \texttt{schema\_version} & \texttt{u16} LE \cat{}
\texttt{u16} LE \\
\tablenums{37..45} & \texttt{offset} & \texttt{u64} LE \\
\tablenums{45..53} & \texttt{compressed\_length} & \texttt{u64} LE \\
\tablenums{53..61} & \texttt{uncompressed\_length} & \texttt{u64} LE \\
\tablenums{61..63} & \texttt{compression} & 2 bytes \\
\tablenums{63..95} & \texttt{hash} & 32 raw digest bytes (restates
\texttt{id}) \\
\bottomrule
\end{longtable}
\endgroup
The canonical sort key for chunk-reference collections is
$(\mathit{kind\ discriminant}, \mathit{hash}, \mathit{offset})$, ascending
--- the Appendix-D chunk-reference order. Every set-valued
\texttt{ChunkRef} field in this chapter is sorted by it and deduplicated
before encoding.
\section{Operation-Envelope Block Payloads}
\label{sec:bundle:blocks}
The uncompressed payload of an \texttt{OperationEnvelopeBlock} chunk:
\begin{center}
\texttt{u32} LE envelope count \cat{} per envelope
\{\texttt{u32} LE byte length \cat{} envelope bytes\}
\end{center}
Envelope bytes are opaque to the bundle (their internal form is
Chapter~\ref{ch:ops}). Each envelope's offset --- the byte offset of its
\emph{first content byte} within the uncompressed block payload, with its
length prefix at $\mathit{offset}-4$ --- is deterministically recoverable
from this framing alone; it is the coordinate the operation index records.
Writers \SHOULD{} begin a new block rather than let an uncompressed payload
exceed 1\,MiB (an individual envelope larger than that occupies its own
block); readers \MUST{} accept blocks up to the active profile's bound
(default 64\,MiB). Block boundaries are storage artifacts: the envelope set
is the union across blocks.
\section{The Manifest}
\label{sec:bundle:manifest}
On-disk manifest chunk payload:
\begin{center}
\texttt{manifest\_id} (16 bytes, \texttt{u128} \emph{little-endian}) \cat{}
\emph{body}
\end{center}
(The little-endian on-disk field is a deliberate, golden-locked quirk: the
\emph{derivation} truncates big-endian like every content-derived id, but
the bundle codec serializes the resulting \texttt{u128} under its
little-endian integer rule.)
\begin{requirement}
\label{req:binfmt:manifest-id}
Restating core requirement \texttt{req:format:manifest-id}, which governs:
\[
\texttt{ManifestId} = \mathrm{trunc128}\bigl(\mathrm{BLAKE3}(
\texttt{MUSCMNIF} \cat \mathit{document\_id} \cat
\mathit{generation} \cat \mathit{body}\bigr)\bigr)
\]
with $\mathit{document\_id}$ as its 16 raw bytes, $\mathit{generation}$ as
\texttt{u64} little-endian, and $\mathit{body}$ the complete manifest body
below (which itself opens with \texttt{document\_id} and
\texttt{generation} --- the double commitment is intentional and locked).
Writers re-derive the id from the body at encode time; decoders verify it.
\end{requirement}
\textbf{The body} is a positional struct of thirteen fields:
\begingroup\small
\begin{longtable}{p{0.3in} p{1.9in} p{3.45in}}
\toprule
\textbf{\#} & \textbf{Field} & \textbf{Encoding and canonical order} \\
\midrule
\endhead
\tablenums{1} & \texttt{document\_id} & 16 raw bytes \\
\tablenums{2} & \texttt{lineage\_id} & option tag \cat{} 16 raw bytes if
present \\
\tablenums{3} & \texttt{generation} & \texttt{u64} LE \\
\tablenums{4} & \texttt{operation\_roots} & \texttt{u32} LE count \cat{}
\texttt{ChunkRef}s, sorted by the canonical chunk-reference key,
deduplicated \\
\tablenums{5} & \texttt{operation\_block\_summaries} & \texttt{u32} LE
count \cat{} entries \{\texttt{chunk\_id} (32) \cat{}
\texttt{OperationBlockSummary}\}, ascending \texttt{chunk\_id} \\
\tablenums{6} & \texttt{operation\_index\_root} & option tag \cat{}
\texttt{ChunkRef} \\
\tablenums{7} & \texttt{canonical\_base} & option tag \cat{}
\texttt{SnapshotRef} \\
\tablenums{8} & \texttt{acceleration\_snapshots} & \texttt{u32} LE count
\cat{} \texttt{SnapshotRef}s, sorted ascending by their full encoded
bytes, deduplicated \\
\tablenums{9} & \texttt{blob\_roots} & \texttt{u32} LE count \cat{}
\texttt{BlobRef}s, sorted ascending by encoded bytes, deduplicated \\
\tablenums{10} & \texttt{profile\_declarations} & \texttt{u32} LE count
\cat{} \texttt{ProfileDeclaration}s, sorted by the key
$(\texttt{profile\_id}, \texttt{version})$ with \texttt{SemVer} compared
\emph{numerically}, deduplicated \\
\tablenums{11} & \texttt{extension\_declarations} & \texttt{u32} LE count
\cat{} \texttt{ExtensionDeclaration}s, sorted by
$(\texttt{extension\_id}, \texttt{version})$, \texttt{SemVer} numeric,
deduplicated \\
\tablenums{12} & \texttt{text\_projection\_root} & option tag \cat{}
\texttt{ChunkRef} \\
\tablenums{13} & \texttt{integrity\_root} & option tag \cat{}
\texttt{ChunkRef} \\
\bottomrule
\end{longtable}
\endgroup
Sub-records, positionally:
\begin{description}
\item[\texttt{OperationBlockSummary}] = \texttt{dvv\_summary}
(\texttt{FrontierBytes}: \texttt{u32}-LE-prefixed opaque bytes) \cat{}
\texttt{min\_stamp} (\texttt{u32}-LE-prefixed opaque stamp bytes) \cat{}
\texttt{max\_stamp} (same). The contents are ops-computed
(Chapter~\ref{ch:ops}); the bundle carries them verbatim.
\item[\texttt{SnapshotRef}] = \texttt{snapshot\_id} (16 raw) \cat{}
\texttt{covers\_causal\_frontier} (\texttt{FrontierBytes}) \cat{}
\texttt{reduction\_algorithm\_version} (\texttt{u32} LE) \cat{}
\texttt{profile\_id} (20 bytes, as in the superblock) \cat{}
\texttt{root} (\texttt{ChunkRef}, 95) \cat{} \texttt{hash} (32 raw).
\item[\texttt{BlobRef}] = \texttt{blob\_id} (32 raw) \cat{}
\texttt{media\_type} (\texttt{u32}-LE-prefixed ASCII; decode validates a
well-formed RFC~6838 \texttt{type/subtype} restricted name) \cat{}
\texttt{offset} (\texttt{u64} LE) \cat{} \texttt{compressed\_length}
(\texttt{u64} LE) \cat{} \texttt{uncompressed\_length} (\texttt{u64} LE)
\cat{} \texttt{compression} (2) \cat{} \texttt{hash} (32 raw) \cat{}
option tag \cat{} \texttt{declared\_max\_uncompressed\_length}
(\texttt{u64} LE) if present.
\item[\texttt{ProfileDeclaration}] = \texttt{profile\_id} (20) \cat{}
\texttt{version} (\texttt{SemVer}, 12) \cat{} \texttt{constraints}
(\texttt{ProfileConstraints} =
\texttt{max\_uncompressed\_block\_size} (\texttt{u64} LE) \cat{}
\texttt{RetentionPolicy}). \texttt{RetentionPolicy} =
\texttt{retain\_previous\_manifests} (\texttt{u32} LE) \cat{} option tag
\cat{} \texttt{retain\_duration} (\texttt{i64} LE) if present \cat{}
\texttt{retain\_named\_checkpoints} (bool, 1).
\item[\texttt{ExtensionDeclaration}] = \texttt{extension\_id} (16 raw)
\cat{} \texttt{version} (\texttt{SemVer}, 12) \cat{} \texttt{required}
(bool, 1) \cat{} \texttt{u32} LE count \cat{}
\texttt{preserved\_chunk\_roots} (\texttt{ChunkRef}s, canonical order,
deduplicated) \cat{} \texttt{affected\_object\_kinds}
(\texttt{u32}-LE-prefixed opaque blob) \cat{} \texttt{edit\_barriers}
(\texttt{u32}-LE-prefixed opaque blob). The two blobs' internal form is
Chapter~\ref{ch:barriers}; the bundle preserves them verbatim.
\end{description}
\begin{requirement}
\label{req:binfmt:manifest-canonical}
\textbf{Reject non-canonical manifests.} A decoder \MUST{} accept a manifest
payload only if re-encoding the decoded manifest reproduces the input
byte-for-byte. This single check subsumes: the stored \texttt{manifest\_id}
matching the body-derived id, every set-valued vector being in its canonical
order with no duplicates, and every optional field using presence byte
\tablenums{0}/\tablenums{1}. Unsorted, duplicated, or wrongly-identified
manifest bytes are malformed --- never re-sorted or repaired.
\end{requirement}
\section{The Operation Index}
\label{sec:bundle:opindex}
\begin{requirement}
\label{req:binfmt:opindex}
\textbf{Operation-index payload (ratifies P12-D1).} The uncompressed payload
of an \texttt{OperationIndex} chunk is:
\begin{center}
\texttt{u32} LE \texttt{block\_count} \cat{} that many \texttt{ChunkRef}s
(95 bytes each, strictly ascending canonical order) \cat{}
\texttt{u32} LE \texttt{entry\_count} \cat{} that many entries, each
\{\texttt{id} (16 raw operation-id bytes) \cat{} \texttt{block}
(\texttt{u32} LE ordinal into the block vector) \cat{} \texttt{offset}
(\texttt{u32} LE)\}, strictly ascending by \texttt{id} bytes.
\end{center}
The empty index is exactly 8 zero bytes. \texttt{offset} is the byte offset
of the envelope's \emph{first content byte} within the uncompressed payload
of the named block (the envelope's \texttt{u32} length prefix sits at
$\mathit{offset}-4$) --- well-defined by the id-leads property
(Requirement~\ref{req:binfmt:envelope}) and the block framing
(Section~\ref{sec:bundle:blocks}).
\textbf{One slot per id.} Strict ascent makes duplicate ids unrepresentable:
an index payload containing two entries with equal id bytes is malformed.
Equivocation candidates (multiple envelopes claiming one
\texttt{OperationId}) live \emph{inside} the operation set's slot model
(core Chapter~6), never as duplicate index rows.
\textbf{Staleness.} An index \emph{covers} a manifest iff its block vector
equals the manifest's deduplicated, canonically sorted
\texttt{operation\_roots} under \textbf{full \texttt{ChunkRef} equality} ---
every field, not just the content hash, since the index hands out its stored
references for reading. A reader that finds the index stale \MUST{} reject
it and rebuild from the blocks.
A rejected or stale index is \textbf{never bundle corruption}: the index is
a non-canonical accelerator, and rebuild-from-blocks is always sound.
Decoders \MUST{} reject: unsorted or duplicated blocks or entry ids, a block
reference whose kind is not \texttt{OperationEnvelopeBlock}, an entry whose
block ordinal is out of range, and trailing bytes.
\end{requirement}
The core specification's commit-time \SHOULD{} --- refresh the index when
the operation log has ``grown significantly'' since it was built --- has an
\textbf{implementation-defined} threshold in v0: this document deliberately
does not pin a number, and conforming writers may use any policy (including
always or never refreshing at commit), because the index is non-canonical.
\begin{openquestion}
Whether to pin a normative refresh threshold (e.g.\ a block-count or
byte-size ratio) or leave it permanently implementation-defined. Leaving it
open costs only lookup performance on stale-index bundles; pinning it too
early would constrain writers for no interoperability gain. Revisit when a
second writer implementation exists.
\end{openquestion}
% ===========================================================================
\chapter{Extension Declaration Blobs and Edit Barriers}
\label{ch:barriers}
The two opaque blobs a manifest \texttt{ExtensionDeclaration} carries ---
\texttt{affected\_object\_kinds} and \texttt{edit\_barriers} --- have their
internal byte form pinned here. This chapter \textbf{ratifies} P12-E1
(the blob and barrier-tree byte form), P12-E2 (the recursion bound), and
P12-E3 (the barrier \texttt{ObjectKind} representation). Barrier
\emph{semantics} --- scope matching, prohibited-edit evaluation,
conservative treatment of unknown extensions --- are core Chapter~8
(\sectionsc{Forward Compatibility and Edit Barriers}); this chapter pins
bytes only.
These layouts are \textbf{regime (b)}: all counts and length prefixes are
\texttt{u64} little-endian --- a deliberate, golden-locked divergence from the
\texttt{u32} regime (Section~\ref{sec:conventions:regimes}). They \textbf{keep
this form in schema major~1}: unlike the resolved-layout surface, these blobs
are carried \emph{opaquely inside the canonical manifest}
(Section~\ref{sec:bundle:manifest}), which stays major~0, so a major-0 reader
must continue to parse them as \texttt{u64}. Their unification waits for a
future major that revises the manifest (Section~\ref{sec:evolution:major1}).
\begin{openquestion}
\textbf{Unsafe-edit tombstone encoding (from P12-E5).} Core Chapter~8 now
ratifies the unsafe-edit tombstone \emph{semantics} (immediate deactivation of
the crossed extension's remaining barriers; a durable record \MUST{} land at
the next commit; \texttt{required = true} interaction). The manifest-side
\emph{byte encoding} of that record is unresolved --- and constrained: the
manifest schema is frozen at major~0, so the record cannot be a new manifest
field. Candidate channels: a marker inside the extension-declaration blob
layer (this chapter's regime-(b) surface), or a dedicated chunk kind
referenced from \texttt{preserved\_chunk\_roots}. To be designed in the next
bundle-format tranche; the editor-side producer
(\texttt{extensions\_requiring\_tombstone()}) already exists.
\end{openquestion}
\section{Framing}
\label{sec:barriers:framing}
\begin{description}
\item[element] $\mathrm{elem}(x)$ = \texttt{u64} LE byte length \cat{}
$x$'s canonical bytes.
\item[set] $\mathrm{set}(xs)$ = elements' canonical byte strings, sorted
ascending byte-lexicographic and \emph{deduplicated}; then \texttt{u64}
LE element count \cat{} each element as $\mathrm{elem}$. Order and
repetition in the source collection cannot affect the bytes; decoders
reject unsorted or duplicated elements.
\item[list] $\mathrm{list}(xs)$ = \texttt{u64} LE count \cat{} each
element as $\mathrm{elem}$, order-significant (used only for condition
subtrees).
\end{description}
\begin{requirement}
\label{req:binfmt:ext-blobs}
\textbf{The two blobs (ratifies P12-E1).} The
\texttt{affected\_object\_kinds} blob is $\mathrm{set}$ of barrier
\texttt{ObjectKind} elements; the \texttt{edit\_barriers} blob is
$\mathrm{set}$ of \texttt{EditBarrier} elements. Both decode under
reject-never-normalize with trailing bytes forbidden.
\end{requirement}
\begin{requirement}
\label{req:binfmt:object-kind-open}
\textbf{Barrier \texttt{ObjectKind} (ratifies P12-E3).} A barrier
\texttt{ObjectKind} is the \texttt{TypedObjectId} discriminant of
Section~\ref{sec:ids:typed-object-id}, encoded as \textbf{2 little-endian
bytes}. Decode is \textbf{open-value}: every \texttt{u16} decodes
successfully --- an unknown kind (a future core kind or an
extension-registered kind) is a \emph{value}, not a decode branch, and
simply never matches any object the reader knows. This is the format's
forward-compatibility stance for barrier kinds: unknown kinds are preserved
and re-emitted verbatim, never dropped or rejected.
\end{requirement}
\section{\texttt{EditBarrier}}
\label{sec:barriers:barrier}
An \texttt{EditBarrier}'s canonical bytes, positionally:
\begin{center}
\texttt{scope} \cat{} $\mathrm{set}$(\texttt{affected\_object\_kinds}:
\texttt{ObjectKind}) \cat{}
$\mathrm{set}$(\texttt{prohibited\_operation\_kinds}:
\texttt{OperationKindTag}, Section~\ref{sec:ops:kind-tag}) \cat{}
\texttt{condition}
\end{center}
\texttt{BarrierScope}: one discriminant byte, then the payload:
\begingroup\small
\begin{longtable}{p{0.35in} p{1.35in} p{3.8in}}
\toprule
\textbf{Disc} & \textbf{Variant} & \textbf{Payload} \\
\midrule
\endhead
\tablenums{0} & \texttt{WholeScore} & (none) \\
\tablenums{1} & \texttt{Region} & \texttt{RegionId}, 16 big-endian bytes \\
\tablenums{2} & \texttt{StaffInstance} & \texttt{StaffInstanceId}, 16 BE \\
\tablenums{3} & \texttt{AnalysisLayer} & \texttt{AnalysisLayerId}, 16 BE \\
\tablenums{4} & \texttt{ObjectSet} & $\mathrm{set}$ of
\texttt{TypedObjectId} canonical bytes (18/34 each) \\
\tablenums{5} & \texttt{PitchSpace} & \texttt{u64} LE byte length \cat{}
the pitch-space id's NFC UTF-8 bytes; decoders reject non-NFC input \\
\tablenums{6} & \texttt{TuningContext} & (none) \\
\tablenums{7} & \texttt{Registered} & \texttt{BarrierScopeRegistryId},
\texttt{u128} \emph{little-endian} (16 bytes) \\
\bottomrule
\end{longtable}
\endgroup
\texttt{BarrierCondition}: one discriminant byte, then the payload; the tree
is recursive:
\begingroup\small
\begin{longtable}{p{0.35in} p{1.85in} p{3.3in}}
\toprule
\textbf{Disc} & \textbf{Variant} & \textbf{Payload} \\
\midrule
\endhead
\tablenums{0} & \texttt{Always} & (none) \\
\tablenums{1} & \texttt{ObjectExists} & \texttt{TypedObjectId} \\
\tablenums{2} & \texttt{ObjectHasExtensionData} & \texttt{object}
(\texttt{TypedObjectId}) \cat{} \texttt{extension}
(\texttt{ExtensionRef}, \texttt{u128} LE, 16) \\
\tablenums{3} & \texttt{All} & $\mathrm{list}$ of conditions \\
\tablenums{4} & \texttt{Any} & $\mathrm{list}$ of conditions \\
\tablenums{5} & \texttt{Not} & one $\mathrm{elem}$-framed condition \\
\tablenums{6} & \texttt{Registered} &
\texttt{BarrierConditionRegistryId}, \texttt{u128} LE (16) \\
\bottomrule
\end{longtable}
\endgroup
Note the endianness: the barrier layer's registry identifiers
(\texttt{BarrierScopeRegistryId}, \texttt{BarrierConditionRegistryId},
\texttt{ExtensionRef}) encode \texttt{u128} \emph{little-endian}, unlike the
big-endian identifier family of Chapter~\ref{ch:ids}. This is part of the
golden-locked regime-(b) surface.
\begin{requirement}
\label{req:binfmt:condition-depth}
\textbf{Recursion bound (ratifies P12-E2).}
$\texttt{MAX\_CONDITION\_DEPTH} = \tablenums{64}$ is the normative bound on
\texttt{BarrierCondition} nesting. Decoders \MUST{} reject a condition tree
nested deeper than 64 levels of \texttt{All}/\texttt{Any}/\texttt{Not};
writers \MUSTNOT{} emit one. The bound exists so adversarial bytes cannot
drive unbounded recursion; real barriers are depth 1--2.
\end{requirement}
% ===========================================================================
\chapter{Schema Evolution}
\label{ch:evolution}
The core specification delegates schema evolution to this document (core
Chapter~8, \sectionsc{Schema Versioning}: \emph{``Schema evolution is
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,
Section~\ref{sec:evolution:major2} the schema-major-2 delta and its
migration from major~1, and Section~\ref{sec:evolution:major3} the
schema-major-3 delta and its migration from major~2.
\section{The Chunk-Level Gate}
\label{sec:evolution:gate}
Every chunk declares a \texttt{SchemaVersion} (major, minor) in its
\texttt{ChunkRef} and the superblock declares the manifest's. The gate:
\begin{itemize}
\item \textbf{Major} = incompatible. A reader \MUST{} reject a chunk whose
schema major it does not support. A reader supports a contiguous accept-set
$[\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}). Four majors are defined:
\tablenums{0}, \tablenums{1} (Section~\ref{sec:evolution:major1}),
\tablenums{2} (Section~\ref{sec:evolution:major2}), and \tablenums{3}
(Section~\ref{sec:evolution:major3}). The accept-set is \textbf{per chunk
role}, not one shared bound (Section~\ref{sec:evolution:major1}'s ``raised
per chunk role'' rule, sharpened by major~3, the first bump under which the
roles' maxima genuinely diverge): the \texttt{Snapshot} role's max is
\tablenums{3}; the \texttt{OperationEnvelopeBlock} role's max stays
\tablenums{2} (schema major~3 embeds no operation payload, so this role
does not rise); every other role's max is \tablenums{0}. The reference
implementation's overall envelope is $\{0, 1, 2, 3\}$ ($\textsc{min} = 0$,
$\textsc{max} = 3$), realized as these differing per-role maxima rather
than a single shared bound.
\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
discriminant appended after the minor it otherwise declares, so that a
decode failure on an unknown appended discriminant is attributable to a
version skew rather than corruption.
\end{itemize}
\section{What ``Additive'' Means Here}
\label{sec:evolution:additive}
Under the frozen-layout rule (Requirement~\ref{req:binfmt:frozen-layout}),
positional struct layouts admit \emph{no} additive change: there is no
optional-field or trailing-field mechanism at the value level. In
particular, and stated honestly: \textbf{the manifest body cannot grow a
fourteenth field within major~0}. The manifest is a positional struct whose
decoder rejects trailing bytes, so manifest-body extension is
major-gated in v0 exactly like every other struct.
Adding a field is a \textsc{major} change \emph{regardless of its type}: an
\texttt{Option} field is not ``optional'' in the wire sense --- it still
occupies a new positional slot (a presence byte, then the payload when
present), which shifts every subsequent field and rejects under the
trailing-bytes rule. There is no ``downgrade to minor'' for an \texttt{Option}
addition. The first exercise of this is schema major~1
(Section~\ref{sec:evolution:major1}), which adds \texttt{Instrument.range}
(an \texttt{Option}) among other fields.
The \emph{only} minor-additive mechanism in schema major~0 is
\textbf{appending discriminants to open vocabularies}:
\begin{itemize}
\item \texttt{OperationKind}: append at ${\geq}\,\tablenums{30}$
(Requirement~\ref{req:binfmt:kind-discriminants}; the Phase-3
tranche took \tablenums{24}--\tablenums{27} under this mechanism,
and the repeat pair \tablenums{28}/\tablenums{29} in the
schema-major-2 revision --- the discriminant space is one append-only
table across majors, but a kind's \emph{block stamp} follows minimal
stamping over its payload: \tablenums{28}'s payload embeds a v2
layout, so its blocks stamp major~2,
Section~\ref{sec:evolution:major2});
\item \texttt{OperationKindTag}: append at ${\geq}\,\tablenums{30}$
(Requirement~\ref{req:binfmt:kind-tag});
\item \texttt{OperationPayload}: append at ${\geq}\,\tablenums{4}$
(Section~\ref{sec:ops:payload});
\item the value-layer unions enumerated as closed in
Requirement~\ref{req:binfmt:frozen-layout} may gain appended variants
only through a ratified revision of this document, also minor-additive.
\end{itemize}
\texttt{ChunkKind} is \textbf{closed} --- it has no \texttt{Registered}
variant and no append story inside major~0, because its discriminant enters
every chunk's hash preimage; a new chunk kind is a format-major event.
Separately, many vocabularies carry a \texttt{Registered} \emph{escape
variant} (\texttt{RepairKind}, \texttt{ReanchorReason},
\texttt{PreconditionFailureReason}, \texttt{IntegrityAnomalyKind},
\texttt{ReplicaAnomalyReason}, \texttt{TransactionCategory},
\texttt{ResolutionAction}, \texttt{ConflictKind} via
\texttt{ExtensionConflict}, \texttt{BarrierScope}/\texttt{BarrierCondition},
\texttt{TieClass}, \texttt{StaffGroupKind}, \texttt{PitchSpacePosition},
\texttt{SpellingNominal}, \texttt{TypedObjectId}, and the barrier
\texttt{ObjectKind}'s open value space). Extension through an escape variant
is \textbf{not} a schema change at all: the wire form is already defined.
\section{Migration Discipline}
\label{sec:evolution:migration}
A schema-\textsc{major} bump \MUST{} ship with a migration documented in
this document's revision history (Chapter~\ref{ch:history}): what changed,
byte-for-byte, and the deterministic translation from the old form. The
precedent is the v0${\to}$v1 operation-payload migration, which lives in the
Operation Catalog (\sectionsc{v0 $\rightarrow$ v1 Payload Migration})
because it was semantic rather than byte-level
(Section~\ref{sec:ops:v0}); byte-level migrations belong here.
The \texttt{u64}/\texttt{u32} length-prefix divergence between regimes (a) and
(b) (Section~\ref{sec:conventions:regimes}), left open in earlier revisions for
a future major, is \textbf{partly resolved in schema major~1}
(Section~\ref{sec:evolution:major1}): the first major bump, scheduled for
data-model reasons, carries the unification for the independent non-canonical
resolved-layout surface. The barrier/extension blobs remain regime~(b) because
they ride the canonical manifest, which this bump keeps at major~0; their
unification stays open for a manifest-revising major.
\section{Schema Major 1}
\label{sec:evolution:major1}
Schema major~1 is the first data-model expansion major, defined here in full:
the changed value layouts, the length-prefix unification, and the
byte-for-byte migration from major~0. It bundles every change that was
deferred to ``the next major'' so that one coordinated migration discharges
them together.
\subsection{Where the changed fields reach}
The three added fields land in different chunk classes, and the major is
assigned \textbf{per payload type}, not per chunk kind:
\begin{itemize}
\item \texttt{Canvas.layout\_defaults} and \texttt{Instrument.range} appear
\emph{only} in the acceleration-cache full-\texttt{Score} snapshot (a
\texttt{ChunkKind::Snapshot}). No operation payload embeds a
\texttt{Canvas} or an \texttt{Instrument} value (there is no
\texttt{CreateCanvas}/\texttt{CreateInstrument}; canvas is the inline
genesis singleton and instruments live in score genesis), so these two
are confined to that one \textbf{non-canonical} chunk.
\item \texttt{Region.permits\_spanning\_slurs} reaches the snapshot
\emph{and} the \textbf{canonical operation layer}:
\texttt{CreateRegion} embeds the full \texttt{Region} value
(Section~\ref{sec:ops:payload}), so its v1 payload carries the v1
\texttt{Region} layout. An operation-envelope block containing a
\texttt{CreateRegion} therefore encodes v1 bytes and is stamped
\textbf{major~1}; a block with no such operation stays \textbf{major~0}.
Operation-envelope blocks are canonical, so this is a canonical
operation-layer change, not merely a cache change.
\item \texttt{ChunkKind::Snapshot} is itself payload-polymorphic: the same
kind carries \emph{either} an acceleration-cache full \texttt{Score} (as
above, stamped major~1) \emph{or} the canonical-base
\texttt{MaterializedState}, disambiguated by manifest role. The canonical
base embeds no \texttt{Canvas}, \texttt{Instrument}, or \texttt{Region}
value, so it is unchanged and stays \textbf{major~0}. Its content-hash
preimage (Section~\ref{sec:bundle:chunks}) includes the schema version, so
re-stamping the unchanged base at major~1 would change its chunk id and
churn every \texttt{SnapshotRef} for zero layout benefit. A writer
\MUSTNOT{} do so; a conformance test \SHOULD{} assert the canonical base
is byte-identical across the bump.
\end{itemize}
\subsection{Cross-major reader behaviour}
Two rules follow from the chunk classes above and the core specification's
\sectionsc{Schema Versioning} canonical/non-canonical distinction:
\begin{itemize}
\item \textbf{Non-canonical chunks --- discard and regenerate.} The layout
caches and the acceleration snapshot are non-canonical. A reader meeting a
foreign-major one \MAY{} discard and regenerate it rather than decode it: a
major-1 reader regenerates a major-0 layout cache instead of carrying a
frozen old-width decoder, and it migrates a major-0 acceleration
\texttt{Score} snapshot on read (cheaper than replaying the operation log).
\item \textbf{Canonical chunks --- parse or open read-only.} The manifest and
the canonical base stay major~0 and are always parseable. A major-1 op
block is canonical: a major-0-only reader \MUSTNOT{} discard it (that would
fork canonical state) --- it opens the bundle in read-only preservation
mode, having read the major-0 base and manifest but being unable to replay
the v1 \texttt{CreateRegion} operations. A major-1 reader migrates such a
block on read (below).
\end{itemize}
So a major-0-only reader opens a major-1 bundle \emph{fully} only when the
bundle contains no v1 \texttt{CreateRegion} operation; otherwise it opens
read-only. It always reads the canonical base and manifest, and always
discards the higher-major non-canonical caches.
\subsection{The accept-set gate}
A reader supports a contiguous set of majors $[\textsc{min}, \textsc{max}]$
(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.
\subsection{Changed and new value layouts}
These extend Chapter~\ref{ch:values}'s regime~(a) rules. As there, \textbf{the
wire form is the reference implementation's struct layout} --- which is a
reduced subset of the core specification's fuller data model (the code's
\texttt{Instrument} carries a subset of the model's fields, etc.). The wire
form ratifies the code, not the model; a struct's v1 layout \textbf{appends}
its new field(s) after its existing major-0 fields:
\begin{itemize}
\item \texttt{Canvas} $=$ \texttt{regions} \cat{} \texttt{layout\_defaults},
where \texttt{CanvasLayoutDefaults} $=$ \texttt{page\_size}
(\texttt{CanvasSize} $=$ \texttt{width} \cat{} \texttt{height}, two
\texttt{CanonicalF64} leaves) \cat{} \texttt{margins}
(\texttt{CanvasMargins} $=$ four \texttt{CanonicalF64} leaves: top,
right, bottom, left).
\item \texttt{Instrument} $=$ \texttt{id} \cat{} \texttt{name} \cat{}
\texttt{range} (\texttt{Option<PitchRange>}: a presence byte, then for
\texttt{Some} a \texttt{PitchRange} $=$ \texttt{lowest} \cat{}
\texttt{highest}, each a \texttt{Pitch} under this chapter's rules).
\item \texttt{Region} appends \texttt{permits\_spanning\_slurs}, one bare
bool byte (\tablenums{0}/\tablenums{1}), after its major-0 fields.
\item \textbf{Operation layer:} the \texttt{CreateRegion} payload embeds a
length-prefixed \texttt{Region} (Section~\ref{sec:ops:payload}), so its v1
form carries the v1 \texttt{Region} layout (the appended bool inside the
embedded value). This is the one \emph{canonical} byte change in major~1;
the operation envelope's own field order is unchanged, only its embedded
\texttt{Region} grows.
\end{itemize}
Every embedded leaf keeps this chapter's framing (a \texttt{CanonicalF64} is a
\texttt{u32} LE length \cat{} 8 bytes $= 12$; a bool is one bare byte).
\subsection{Length-prefix unification (partial)}
Regime~(b) (Section~\ref{sec:conventions:regimes}) is \emph{narrowed}, not
fully retired, in major~1. The \texttt{ResolvedLayoutIR} output --- an
independent, non-canonical \texttt{LayoutCache} chunk --- moves from
\texttt{u64} to \texttt{u32} LE counts and length prefixes, matching
regime~(a), and its golden anchors re-lock in the same step. Because it is
non-canonical, a major-1 reader regenerates a major-0 resolved-layout cache
rather than decoding its \texttt{u64} form --- no frozen \texttt{u64} decoder
is required.
The extension-declaration and edit-barrier blobs (Chapter~\ref{ch:barriers})
\textbf{stay regime~(b)}: they are carried opaquely inside the canonical
manifest (Section~\ref{sec:bundle:manifest}), which stays major~0, so a
major-0 reader must keep parsing them as \texttt{u64}. Unifying them would
require bumping the manifest itself, which this data-model bump deliberately
avoids; that unification waits for a manifest-revising major.
\subsection{Migration from major~0}
The v0${\to}$v1 translation is total and default-filling: every new field has
a canonical default, so no value is unrecoverable --- including the
\texttt{CreateRegion} operation payload, whose embedded \texttt{Region}
default-fills structurally. This is unlike the \emph{semantic},
context-dependent operation-payload migration of Section~\ref{sec:ops:v0}
(which could be irreversible); the major-1 migration needs no score context.
\begin{center}
\begin{tabular}{p{1.9in} p{3.4in}}
\toprule
\textbf{major-0 form} & \textbf{major-1 form} \\
\midrule
\texttt{Canvas} $=$ \texttt{regions} &
append \texttt{layout\_defaults} $=$ the A4/8\,mm default (page
$105 \times 148.5$, margins $7.5$ staff spaces) \\
\texttt{Instrument} $=$ \texttt{id}, \texttt{name} &
append \texttt{range} $=$ \texttt{None} (presence byte \tablenums{0}) \\
\texttt{Region} $= \ldots$ (snapshot) &
append \texttt{permits\_spanning\_slurs} $=$ \tablenums{0}
(\texttt{false}) \\
\texttt{CreateRegion} op payload &
default-fill the embedded \texttt{Region}'s appended field
(\texttt{permits\_spanning\_slurs} $=$ \tablenums{0}); \emph{canonical} ---
a v0 op block migrates on read, and a block bearing a v1 \texttt{CreateRegion}
is major~1 (a major-0-only reader opens the bundle read-only) \\
\texttt{ResolvedLayoutIR} \texttt{u64} prefixes &
non-canonical \texttt{LayoutCache}: discard and regenerate at major~1
under \texttt{u32} (no in-place byte translation) \\
barrier / extension-declaration blobs &
stay regime~(b) \texttt{u64} (manifest-embedded, manifest stays major~0);
unchanged in this bump \\
canonical-base \texttt{MaterializedState} &
unchanged, byte-identical, stays major~0 \\
\bottomrule
\end{tabular}
\end{center}
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); it reaches only the acceleration full-\texttt{Score} snapshot.
(\texttt{RepeatStructure} was snapshot-only when this section was
first ratified; the repeat-authoring pair moved it to the canonical
operation layer --- next bullet.)
\item \textbf{Canonical operation layer:} eight operation
payloads embed filled values. \texttt{CreateRepeatStructure} (this
revision --- Operation Catalog \sectionsc{Repeat Structures},
discriminant \tablenums{28}) embeds the v2 \texttt{RepeatStructure}
layout unconditionally (\texttt{kind} and \texttt{voltas} are not
optional fields) and is \emph{born at v2}: no lower-major layout for
its payload exists, so every block carrying one stamps major~2 under
minimal stamping. Its sibling \texttt{DeleteRepeatStructure}
(\tablenums{29}) carries a bare identifier --- a major-0 layout ---
so minimal stamping gives its blocks major~0; the kind discriminant
itself is an append-only schema-\emph{minor} vocabulary event (the
same append mechanism the Phase-3 tranche used for
\tablenums{24}--\tablenums{27} --- a mechanism claim only: what a
kind's blocks \emph{stamp} always follows minimal stamping over its
payload, as discriminant \tablenums{24}'s always-major-2
\texttt{CreateStaff} shows). Neither changes anything about
lower-major layouts. The seven payloads of the first ratification
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};
\texttt{CreateRepeatStructure} $\Rightarrow$ always \textbf{2}
(born at v2); \texttt{DeleteRepeatStructure} $\Rightarrow$
\textbf{0} (a bare identifier under a minor kind append);
\texttt{TransposeInterval} $\Rightarrow$ \textbf{0} (a sequence of
identifiers and two \texttt{i32}s --- every constituent is a major-0
layout, so the kind append is a schema \emph{minor}, and a
\texttt{TransposeInterval} rides in a major-0 block); 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}
\section{Schema Major 3}
\label{sec:evolution:major3}
Schema major~3 is the third data-model expansion major (Push 4b tranche
3b-i, \sectionsc{Score Tuning Context}): \texttt{ScoreTuningContext} gains
\texttt{smufl} (the score's declared SMuFL version requirement) and
\texttt{overrides} (per-scope tuning overrides) on the canonical wire.
\texttt{accidental\_extensions} --- the third field the core specification
added alongside these two --- is deliberately \textbf{staged} to a later
major: its consumer, the engraver, does not exist yet, and freezing a wire
layout ahead of a real consumer is exactly what the frozen-layout rule
(Requirement~\ref{req:binfmt:frozen-layout}) warns against. When
\texttt{accidental\_extensions} does land, it appends \emph{after}
\texttt{overrides}, per the append-only rule this chapter has followed since
major~1.
\subsection{Where the changed fields reach}
Only one payload type changes, and it changes at exactly one site:
\begin{itemize}
\item \textbf{Snapshot-only:} \texttt{ScoreTuningContext} reaches the wire
\emph{only} through the acceleration full-\texttt{Score} snapshot. No
operation payload embeds the tuning context (core Chapter~6's operation
vocabulary carries no \texttt{ScoreTuningContext} / \texttt{TuningOverride}
/ \texttt{SmuflVersionRequirement} field anywhere), so the
\textbf{canonical operation layer is untouched}: no op payload is ``born
at v3'', and no frozen v2 op-payload decoder is needed. The
\texttt{OperationEnvelopeBlock} role's accept-set therefore stays at
major~2 (Section~\ref{sec:evolution:major2}) --- schema major~3 is the
first data-model bump under which a chunk role's max does \emph{not}
move in lockstep with the others.
\item \textbf{The canonical base is unchanged.} The
\texttt{MaterializedState} embeds no tuning context, so it remains
major~0, byte-identical across the bump --- the same keystone as
majors~1 and~2, 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/major-2 rules extend unchanged: the acceleration snapshot is
non-canonical, so a reader \MAY{} discard and regenerate a foreign-major one,
or migrate it on read (the reference implementation migrates). Canonical
chunks parse or the bundle opens read-only. Migration \emph{composes}: a
major-3 reader migrates a major-0 snapshot
v0${\to}$v1${\to}$v2${\to}$v3 in one read, each step total and
default-filling. Because no operation payload changes at this major, a
major-2-only reader's op-block admission is exactly as before this bump ---
only the \texttt{Snapshot} role's accept-set widens, to \tablenums{3}
(Section~\ref{sec:evolution:gate}).
\subsection{Changed value layout}
As in majors~1 and~2, the wire form appends new fields \emph{after} the
existing ones, independent of the core specification's presentational field
order (Section~\ref{sec:evolution:major1}'s note applies unchanged):
\begin{itemize}
\item \texttt{ScoreTuningContext} $=$ \texttt{default\_pitch\_space} \cat{}
\texttt{default\_tuning\_system} \cat{} \texttt{reference} (the frozen
major-0/1/2 prefix, unchanged since major~0) \cat{}
\textbf{\texttt{smufl}} \cat{} \textbf{\texttt{overrides}}.
\texttt{accidental\_extensions} is \textbf{not} on the wire this major
(see above): a decoder default-fills it, exactly as it always has, and a
future major appends it after \texttt{overrides} when it lands.
\end{itemize}
\subsection{New leaf-type layouts}
\begin{itemize}
\item \texttt{SmuflVersion} $=$ \texttt{major} (\texttt{u16} LE, bare) \cat{}
\texttt{minor\_centi} (\texttt{u16} LE, bare) --- the fractional SMuFL
version digits normalized to hundredths (1.12 $\to$ \tablenums{12}, 1.4
$\to$ \tablenums{40}, 1.3 $\to$ \tablenums{30}) so an integer comparison
on the pair agrees with SMuFL's real release order (1.12 shipped before
1.3, though \tablenums{3} literally sorts before \tablenums{12}).
\item \texttt{SmuflVersionRequirement} $=$ \texttt{minimum}
(\texttt{SmuflVersion}) \cat{} \texttt{authored\_against}
(\texttt{SmuflVersion}) --- $8$ bytes total, neither field framed as a
length-prefixed leaf (both are fixed-shape bare-integer pairs).
\item \texttt{TuningScope}: one discriminant byte \cat{} payload ---
\tablenums{0} Voice \cat{} \texttt{VoiceId}; \tablenums{1} Staff \cat{}
\texttt{StaffId}; \tablenums{2} Region \cat{} \texttt{RegionId};
\tablenums{3} Range \cat{} \texttt{start} (\texttt{TimeAnchor}) \cat{}
\texttt{end} (\texttt{TimeAnchor}) \cat{} \texttt{voices}
(\texttt{VoiceSelector}), fields in declaration order.
\item \texttt{TuningOverride} $=$ \texttt{scope} (\texttt{TuningScope})
\cat{} \texttt{pitch\_space} (\texttt{Option<PitchSpaceId>}) \cat{}
\texttt{tuning\_system} (\texttt{Option<TuningSystemId>}) \cat{}
\texttt{reference} (\texttt{Option<ReferencePitch>}) --- one presence
byte per \texttt{Option}, the inner catalog-id/pitch leaves under this
chapter's regime~(a) framing (a catalog id is a length-prefixed string;
\texttt{ReferencePitch} carries its own established layout).
\end{itemize}
\subsection{Migration from major 2}
Total and default-filling, needing no score context; composes after the
major-1 and major-2 migrations for major-1/major-0 input. The flat $12$
default bytes this migration appends (\texttt{smufl} $= 8$,
\texttt{overrides}' empty count $= 4$) are the same for every score,
independent of its other content, since neither field depends on anything
already on the wire.
\begin{center}
\begin{tabular}{p{1.9in} p{3.4in}}
\toprule
\textbf{major-2 form} & \textbf{major-3 form} \\
\midrule
\texttt{ScoreTuningContext} & append \texttt{smufl} $=$ $1.4$/$1.4$ (both
\texttt{minimum} and \texttt{authored\_against}, the version this
reference implementation already targets), \texttt{overrides} $=$ empty
\\
canonical-base \texttt{MaterializedState} & unchanged, byte-identical,
stays major~0 \\
\bottomrule
\end{tabular}
\end{center}
% ===========================================================================
\chapter{Non-Canonical Pinned Encodings}
\label{ch:noncanon}
\textbf{Non-canonical} means: never part of document identity. Nothing in
this chapter enters a content hash that canonical state depends on, and all
of it can be discarded and rebuilt without changing the document. The
encodings are pinned for \emph{reproducibility} --- byte-equal conformance
claims and cache correctness --- not for durability.
\section{\texttt{LayoutObjectId} (\texttt{MUSCLOID})}
\label{sec:noncanon:muscloid}
Governed by core requirement \texttt{req:layoutir:object-id-derivation}.
$\texttt{LayoutObjectId} = \mathrm{trunc128}(\mathrm{BLAKE3}(
\texttt{MUSCLOID} \cat \mathit{inputs}))$, with three preimage shapes:
\begin{description}
\item[stable] $\mathit{inputs}$ = the source
\texttt{TypedObjectId}'s canonical bytes.
\item[manifestation] $\mathit{inputs}$ = source canonical bytes \cat{}
\texttt{RegionId} canonical bytes (16 BE) --- distinct regions give one
source distinct manifestation ids.
\item[synthesized] $\mathit{inputs}$ = source canonical bytes \cat{}
five \texttt{u64} LE words: the \texttt{SynthesisKind} discriminant,
the registry id's high and low 64 bits (zero unless
\texttt{Registered}), and the instance key's high and low 64 bits.
\end{description}
\texttt{SynthesisKind} discriminants: \tablenums{0} CancellationAccidental,
\tablenums{1} KeySignatureNatural, \tablenums{2} GeneratedRest,
\tablenums{3} EngravedBreak, \tablenums{4} MultimeasureRest,
\tablenums{5} Cautionary, \tablenums{6} Registered.
\section{\texttt{ResolvedLayoutIR} Canonical Output}
\label{sec:noncanon:resolved}
The resolved layout's canonical bytes are the \textbf{byte-equal conformance
surface} of core Chapter~7: two conforming engravers given the same score,
profile, and glyph catalog must produce identical bytes. In schema major~0 the
encoding is regime (b) (\texttt{u64} LE counts and length prefixes); schema
major~1 moves it to regime (a)'s \texttt{u32} prefixes with the rest of the
unification (Section~\ref{sec:evolution:major1}), re-locking this surface's
goldens. Because the surface is non-canonical, a reader regenerates a
foreign-major layout cache rather than decoding it. Either way every geometric
coordinate is quantized to the $1/1024$ staff-space grid as a
\texttt{QuantizedCoord} (8 LE bytes; Chapter~\ref{ch:primitives});
a non-finite or out-of-range coordinate is a determinism violation and
\MUST{} be rejected, never normalized. The top-level section order is:
source score version \cat{} pages \cat{} glyphs \cat{} strokes \cat{}
engraving decisions \cat{} glyph-catalog identity; within each section,
elements carry their provenance (built on \texttt{MUSCLOID} ids) and their
quantized geometry. The full leaf grammar is pinned by the reference
implementation's golden and round-trip anchors (Chapter~\ref{ch:goldens})
rather than restated field-by-field here, because the surface is
non-canonical and evolves with the engraving feature set.
\section{Layout Caches and Font Metrics}
\label{sec:noncanon:caches}
\texttt{LayoutCache} chunks (\texttt{ChunkKind} \tablenums{6}) hold cached
layout artifacts; they are \textbf{always discardable}, and a reader
\MUSTNOT{} treat a missing, stale, or undecodable layout cache as bundle
corruption. The glyph-metrics identity hash is domain-separated under
\texttt{MUSCFNTM} (a reserved built-in, non-canonical tag): it names the
metrics a layout was computed against, so caches and conformance runs can
detect font drift; it never enters canonical state.
% ===========================================================================
\chapter{Golden Anchor Registry}
\label{ch:goldens}
The conformance contract binding this document to the reference
implementation. Three lock classes:
\begin{description}
\item[literal-byte] the test asserts exact expected bytes or exact
discriminant literals --- the strongest lock;
\item[round-trip] the test asserts $\mathrm{decode} \circ \mathrm{encode} =
\mathrm{id}$ and re-encode byte-stability over a corpus;
\item[canonical-equality] the test asserts that semantically equal values
encode identically (order-independence, reduction identities).
\end{description}
The first block restates the anchors already recorded in the core
specification's \texttt{app:bytes} \sectionsc{Reference-implementation
locks} table (which governs them); the second block adds the anchors this
document introduces, which \texttt{app:bytes} predates.
Each anchor names its crate file, then the test on a second line.
\begingroup\footnotesize
\begin{longtable}{>{\raggedright\arraybackslash}p{1.5in} >{\raggedright\arraybackslash}p{3.0in} p{1.0in}}
\toprule
\textbf{Layout} & \textbf{Anchor} & \textbf{Class} \\
\midrule
\endhead
\multicolumn{3}{l}{\emph{Imported from \texttt{app:bytes}:}} \\
\addlinespace[2pt]
\texttt{TypedObjectId} &
\texttt{epiphany-core/src/ids.rs}\newline
\texttt{::typed\ub object\ub id\ub byte\ub form\ub is\ub locked} &
literal-byte \\
Promoted \texttt{VoiceId} &
\texttt{epiphany-core/src/graph.rs}\newline
\texttt{::promoted\ub voice\ub id\ub byte\ub form\ub is\ub locked} &
literal-byte \\
System \texttt{PitchId} &
\texttt{epiphany-core/src/pitch.rs}\newline
\texttt{::system\ub pitch\ub id\ub byte\ub form\ub is\ub locked} &
literal-byte \\
\texttt{IntegrityAnomalyId} &
\texttt{epiphany-ops/src/anomaly.rs}\newline
\texttt{::integrity\ub anomaly\ub id\ub byte\ub form\ub is\ub locked} &
literal-byte \\
\texttt{ObjectKind} (ops) &
\texttt{epiphany-ops/src/support.rs}\newline
\texttt{::object\ub kind\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{ChunkKind} &
\texttt{epiphany-bundle/src/chunk.rs}\newline
\texttt{::chunk\ub kind\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{Compression\allowbreak Algorithm} &
\texttt{epiphany-bundle/src/chunk.rs}\newline
\texttt{::compression\ub algorithm\ub encoding\ub is\ub golden} &
literal-byte \\
\texttt{ProfileId} &
\texttt{epiphany-bundle/src/superblock.rs}\newline
\texttt{::profile\ub id\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{ManifestId} &
\texttt{epiphany-bundle/src/ids.rs}\newline
\texttt{::manifest\ub id\ub is\ub content\ub derived\ub and\ub deterministic} &
canonical-equality \\
\texttt{TransactionCategory} &
\texttt{epiphany-ops/src/payload.rs}\newline
\texttt{::transaction\ub category\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{ResolutionAction} &
\texttt{epiphany-ops/src/conflict.rs}\newline
\texttt{::resolution\ub action\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{BlobId} &
\texttt{epiphany-determinism/src/hash.rs}\newline
\texttt{ContentHash::of\ub blob} tests &
round-trip \\
\texttt{RationalTime} &
\texttt{epiphany-core/src/time.rs}\newline
\texttt{::equal\ub rationals\ub encode\ub identically} &
canonical-equality \\
\texttt{MUSCLOID} derivation &
\texttt{epiphany-layout-ir/src/provenance.rs}\newline
\texttt{::stable\ub id\ub uses\ub the\ub ratified\ub muscloid\ub derivation} &
literal-byte \\
Domain-tag spellings &
\texttt{epiphany-determinism/src/domain.rs}\newline
\texttt{::exact\ub tag\ub spellings\ub match\ub spec} &
literal-byte \\
\addlinespace[4pt]
\multicolumn{3}{l}{\emph{Added by this document:}} \\
\addlinespace[2pt]
\texttt{OperationKind} discriminants (\S\ref{sec:ops:kinds}) &
\texttt{epiphany-ops/src/payload.rs}\newline
\texttt{::operation\ub kind\ub wire\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{OperationPayload} discriminants (\S\ref{sec:ops:payload}) &
\texttt{epiphany-ops/src/payload.rs}\newline
\texttt{::operation\ub payload\ub discriminants\ub are\ub golden} &
literal-byte \\
\texttt{OperationKindTag} (\S\ref{sec:ops:kind-tag}) &
\texttt{epiphany-ops/src/payload.rs}\newline
\texttt{::operation\ub kind\ub tag\ub decode\ub mirrors\ub encode\ub exactly} &
round-trip \\
\texttt{ResolveEquivocation} payload (\S\ref{sec:ops:payload}) &
\texttt{epiphany-ops/src/payload.rs}\newline
\texttt{::resolve\ub equivocation\ub payload\ub encodes\ub target\ub then\ub hash} &
literal-byte \\
\texttt{OperationStamp} (\S\ref{sec:ops:stamp}) &
\texttt{epiphany-ops/src/stamp.rs}\newline
\texttt{::stamp\ub encode\ub is\ub stable} &
literal-byte \\
\texttt{CausalContext} (\S\ref{sec:ops:causal}) &
\texttt{epiphany-ops/src/causal.rs}\newline
\texttt{::canonical\ub encoding\ub is\ub build\ub order\ub independent} &
canonical-equality \\
\texttt{MaterializedState} (\S\ref{sec:ops:materialized}) &
\texttt{epiphany-ops/src/decode.rs}\newline
\texttt{::reduced\ub states\ub decode\ub and\ub reencode} &
round-trip \\
Operation-index payload (\S\ref{sec:bundle:opindex}) &
\texttt{epiphany-bundle/src/opindex.rs}\newline
\texttt{::payload\ub encoding\ub is\ub golden} &
literal-byte \\
Fixed header (\S\ref{sec:bundle:header}) &
\texttt{epiphany-bundle/src/header.rs}\newline
\texttt{::header\ub round\ub trips} &
round-trip \\
Superblock (\S\ref{sec:bundle:superblock}) &
\texttt{epiphany-bundle/src/superblock.rs}\newline
\texttt{::superblock\ub round\ub trips\ub through\ub 256\ub bytes} &
round-trip \\
Chunk hash preimage (\S\ref{sec:bundle:chunks}) &
\texttt{epiphany-bundle/src/chunk.rs}\newline
\texttt{::hash\ub preimage\ub matches\ub the\ub spec\ub layout} &
literal-byte \\
\texttt{ChunkRef} (\S\ref{sec:bundle:chunkref}) &
\texttt{epiphany-bundle/src/chunk.rs}\newline
\texttt{::chunk\ub ref\ub round\ub trips} &
round-trip \\
Manifest body (\S\ref{sec:bundle:manifest}) &
\texttt{epiphany-bundle/src/manifest.rs}\newline
\texttt{::re\ub encode\ub is\ub byte\ub identical},
\texttt{::manifest\ub round\ub trips},
\texttt{::duplicate\ub roots\ub collapse\ub on\ub encode},
\texttt{::semver\ub orders\ub numerically\ub not\ub byte\ub wise} &
round-trip + canonical-equality \\
Barrier kinds blob (\S\ref{sec:barriers:framing}) &
\texttt{epiphany-layout-ir/src/barrier.rs}\newline
\texttt{::affected\ub object\ub kinds\ub blob\ub bytes\ub are\ub golden} &
literal-byte \\
Barriers blob (\S\ref{sec:barriers:barrier}) &
\texttt{epiphany-layout-ir/src/barrier.rs}\newline
\texttt{::edit\ub barriers\ub blob\ub bytes\ub are\ub golden} &
literal-byte \\
Barrier variants (\S\ref{sec:barriers:barrier}) &
\texttt{epiphany-layout-ir/src/barrier.rs}\newline
\texttt{::every\ub scope\ub and\ub condition\ub variant\ub round\ub trips\ub byte\ub identically} &
round-trip \\
Whole-\texttt{Score} codec (\S\ref{sec:values:score}) &
\texttt{epiphany-core/src/codec.rs}\newline
\texttt{::generator\ub scores\ub round\ub trip},
\texttt{::trailing\ub and\ub truncated\ub bytes\ub are\ub rejected} &
round-trip \\
\texttt{CanonicalValue} seam (\S\ref{sec:values:canonical-value}) &
\texttt{epiphany-core/src/codec.rs}\newline
\texttt{::value\ub types\ub round\ub trip\ub over\ub generator\ub corpus} &
round-trip \\
\bottomrule
\end{longtable}
\endgroup
Struct \emph{bodies} in Chapter~\ref{ch:values} are round-trip-locked rather
than literal-byte-locked: their byte identity follows from the frozen
positional rule plus the literal-byte locks on every discriminant and leaf
they embed.
\section{The Decode Vector Corpus}
\label{sec:goldens:decode-vectors}
The cross-implementation decoder test is \textbf{delivered}, as a corpus of
literal byte strings each paired with the verdict a decoder owes it. The
reference implementation carries it at \texttt{spec/vectors/decode\_vectors.txt}
and regenerates it from a committed generator, so a change to any wire form
moves the file.
Each line is \texttt{surface\ \ verdict\ \ class\ \ name\ \ hex}. The
\texttt{verdict} is \texttt{accept} or \texttt{reject}. The \texttt{class}
names \emph{why} a rejected vector is rejected and is \textbf{informative
only}: implementations need not agree on an error taxonomy.
\begin{requirement}
\label{req:binfmt:decode-vectors}
A conforming decoder \MUST{} accept every \texttt{accept} vector for a
surface it implements, and \MUST{} reject every \texttt{reject} vector for
that surface. A value decoded from an \texttt{accept} vector \MUST{}
re-encode to exactly that vector's bytes.
Accepting a \texttt{reject} vector and then normalizing it is
\emph{accepting} it, and does not satisfy this requirement. Canonical decode
is injective: distinct byte strings denote distinct values, which is what
content-addressing rests on.
\end{requirement}
\begin{rationale}
The corpus is not a restatement of this document; it is what a second
implementation can be run against. Every rejection class in it is one the
reference implementation shipped a bug in, or one whose check no injectivity
fuzzer can see: a whole-value re-encode guard catches the fields a decoder
\emph{normalizes} and is blind to order-preserving sequence fields, which need
per-site order checks; and a guard on an outer value can \emph{mask} a lenient
inner codec rather than fix it (Push~5).
A future revision should extend the corpus to the representative struct
layouts of Section~\ref{sec:values:representative}, which remain round-trip
locked rather than literal-byte locked.
\end{rationale}
% ===========================================================================
\chapter{Revision History}
\label{ch:history}
\begin{longtable}{p{2cm} p{2.5cm} p{9cm}}
\toprule
\textbf{Date} & \textbf{Section} & \textbf{Change} \\
\midrule
\endhead
\today & All & 0.1.0 --- Initial companion: ratifies the previously
provisional whole-\texttt{Score} value codec (core P11-4), operation-layer
wire forms (ops), bundle physical layout (bundle P11-D2/D4/D5),
operation-index payload (P12-D1), and extension-blob/edit-barrier byte
forms (P12-E1/E2/E3); pins the no-varint rule, the frozen-positional
schema-evolution keystone, and the id-leads envelope property;
\texttt{SnapshotId} derivation deferred (open question). \\
\today & Operation wire forms & 0.2.0 --- Phase-3 first tranche: appended
\texttt{OperationKind} wire discriminants \tablenums{24}--\tablenums{27}
(\texttt{CreateStaff}, \texttt{SetTimeSignature},
\texttt{SetTempoSegment}, \texttt{SetStaffLayout}) with their payload
layouts, the matching \texttt{OperationKindTag} discriminants
\tablenums{24}--\tablenums{27} (kind-to-tag naming gains
\texttt{CreateStaff}~$\rightarrow$~\texttt{InsertStaff}), and
\texttt{PreconditionFailureReason} \tablenums{11}
(\texttt{TempoMapMalformed}) --- a schema-\emph{minor} evolution under this
document's own append-only rules (Chapter~\ref{ch:evolution}); no existing
assignment changed. Semantics: Operation Catalog 0.5.0 (\sectionsc{CreateStaff},
\sectionsc{Meter and Tempo Overwrites}, \sectionsc{SetStaffLayout}, and the
value-restoring \sectionsc{UndoTransaction} revision). \\
\today & Schema evolution / Graph value layouts & 0.3.0 --- Defines
\textbf{schema major~1}, the first data-model expansion major
(Section~\ref{sec:evolution:major1}): appends \texttt{Canvas.layout\_defaults}
(new \texttt{CanvasLayoutDefaults}/\texttt{CanvasSize}/\texttt{CanvasMargins}
layouts, snapshot-only), \texttt{Instrument.range} (\texttt{Option<PitchRange>},
snapshot-only), and \texttt{Region.permits\_spanning\_slurs} (also inside the
\emph{canonical} \texttt{CreateRegion} operation payload); narrows regime~(b)
by unifying the non-canonical resolved-layout length prefixes to \texttt{u32}
(the manifest-embedded barrier blobs stay \texttt{u64}, partly resolving the
standing open question); pins per-payload-type major assignment (the
canonical-base \texttt{MaterializedState} stays major~0, byte-identical),
the accept-set gate ($[\textsc{min},\textsc{max}]$), the cross-major reader
rules (discard-and-regenerate non-canonical chunks; parse-or-read-only for
canonical ones, so a major-0 reader opens a bundle with v1 \texttt{CreateRegion}
ops read-only), and the total default-filling v0${\to}$v1 migration table.
Clarifies that any field add is a major change regardless of
\texttt{Option}-ness. \\
\today & Effect vocabularies / Extension blobs & 0.4.0 --- Pass-12
G-ratification: appends \texttt{PreconditionFailureReason}
\tablenums{12} (\texttt{SystemDerivedContentImmutable}, P12-K3) and
\tablenums{13} (\texttt{RecreateContentMismatch}, P12-K9), and
\texttt{ReanchorReason} \tablenums{6} (\texttt{SameCanvasNearer},
P12-C4) --- schema-\emph{minor} appends under Chapter~\ref{ch:evolution};
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). \\
\today & Operation vocabularies & 0.6.0 --- Repeat authoring
(schema-major-2 revision, Phase D): appends \texttt{OperationKind}
\tablenums{28} (\texttt{CreateRepeatStructure},
$\mathrm{lp}$(\texttt{RepeatStructure}), born at v2 --- every block
carrying one stamps major~2 under minimal stamping) and \tablenums{29}
(\texttt{DeleteRepeatStructure}, bare identifier --- minimal stamping
over its major-0 payload layout gives major~0; the discriminant itself
a schema-\emph{minor} vocabulary append), with the
matching \texttt{OperationKindTag} \tablenums{28}/\tablenums{29}
(name-verbatim projection). No existing assignment changed;
Section~\ref{sec:evolution:major2} restates the per-payload stamping
with the pair. Semantics: Operation Catalog \sectionsc{Repeat
Structures}, 0.7.0; re-anchoring row (nearest-surviving-anchor across
every anchor site, cascade when none): core Chapter~6 rule table. \\
\today & Operation vocabularies & 0.7.0 --- Transpose algebra (Push~4a):
appends \texttt{OperationKind} \tablenums{30}
(\texttt{TransposeInterval}: $\mathrm{seq}^{\Uparrow}$(\texttt{PitchId})
\cat{} two \texttt{i32}s) and the matching \texttt{OperationKindTag}
\tablenums{30}. Every constituent is a major-0 layout, so the payload
stamps major~\tablenums{0} and the discriminant is a schema-\emph{minor}
vocabulary append --- no major~3. Introduces the
$\mathrm{seq}^{\Uparrow}$ notation (strictly increasing; a decoder
\MUST{} reject a duplicate rather than normalize it) and marks
\tablenums{9} (\texttt{Transpose}) frozen, its
$\mathrm{seq}^{\uparrow}$ being a multiset. No existing assignment
changed. Semantics: Operation Catalog \sectionsc{TransposeInterval},
0.8.0; algebra: core Chapter~2, \sectionsc{Transposition and the
Interval Type}. \\
\today & Chunk references & 0.8.0 --- Strict \texttt{CompressionAlgorithm}
decode (Push~5, P3): \texttt{req:binfmt:compression-none-parameter} \MUST{}
reject a non-zero parameter byte on the \texttt{None} discriminant. Ignoring
it made the codec non-injective --- two byte strings, one value --- which
every structure embedding a \texttt{ChunkRef} inherited; the operation index,
having no whole-value re-encode guard, accepted both. Found by the wire-decode
fuzzer. Supersedes the core specification's ``ignored on read''; no wire
layout changed and no conforming writer is affected. \\
\today & Golden anchors & 0.9.0 --- Ratifies the decode vector corpus
(Push~5, P4): \texttt{req:binfmt:decode-vectors} and
Section~\ref{sec:goldens:decode-vectors}. The cross-implementation decoder
test moves out of the \sectionsc{About This Companion} exclusion list and out
of the \sectionsc{Golden Anchor Registry}'s ``deferred conformance harness''
note: literal byte vectors with accept/reject verdicts now exist
(\texttt{spec/vectors/decode\_vectors.txt}), and a conforming decoder owes them
the verdict. Accepting a \texttt{reject} vector and normalizing it is
\emph{accepting} it. The wire-format fuzzer remains an implementation
deliverable. No wire layout changed. \\
\today & Schema evolution / Graph value layouts & 0.10.0 --- Defines
\textbf{schema major~3} (Section~\ref{sec:evolution:major3}, Push~4b
tranche 3b-i): \texttt{ScoreTuningContext} gains \texttt{smufl}
(\texttt{SmuflVersionRequirement}) and \texttt{overrides}
($\mathrm{seq}$(\texttt{TuningOverride})) on the canonical wire, with
layouts for the four new leaf types (\texttt{SmuflVersion},
\texttt{SmuflVersionRequirement}, \texttt{TuningScope},
\texttt{TuningOverride}) and the total default-filling v2${\to}$v3
migration table. \texttt{accidental\_extensions} is deliberately staged to
a later major (no consumer yet) and stays off the wire. Reaches only the
acceleration full-\texttt{Score} snapshot --- no operation payload embeds
the tuning context, so the canonical operation layer and its accept-set
are untouched (the first data-model major under which a chunk role's max
does not move in lockstep with the others); the canonical base and
manifest stay major~0, byte-identical. Accept-set: \texttt{Snapshot}
widens to \tablenums{3}, \texttt{OperationEnvelopeBlock} stays at
\tablenums{2}. Semantics: core specification Chapter~4, \sectionsc{Score
Tuning Context} / \sectionsc{SMuFL Versioning}; the staging ruling itself
is Push~4b's own (\texttt{spec/CONTRACT\_PUSH4B\_3BI\_WIRE.md}). \\
\bottomrule
\end{longtable}
\end{document}