% !TEX program = xelatex % % Epiphany --- Core Specification % A specification document for the Epiphany music notation platform. % Compile with XeLaTeX. \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{graphicx} \usepackage{caption} \usepackage{subcaption} \usepackage{tikz} \usepackage{float} \usepackage{tcolorbox} \tcbuselibrary{breakable, skins} % --------------------------------------------------------------------------- % Color palette: refined classical-modern % - Deep teal as the primary structural color % - Antique gold as the accent and titling color % - Warm parchment-tinted neutrals % --------------------------------------------------------------------------- \definecolor{epiphanyteal}{HTML}{1A4044} % deep teal --- primary \definecolor{epiphanygold}{HTML}{8E6E2E} % antique gold --- accent \definecolor{epiphanyink}{HTML}{1F1B16} % warm near-black for body text \definecolor{epiphanyslate}{HTML}{6B6660} % warm slate gray \definecolor{epiphanycream}{HTML}{F8F4ED} % parchment cream \definecolor{epiphanymist}{HTML}{ECE8E0} % slightly deeper parchment \definecolor{epiphanycode}{HTML}{2A2520} % warm dark for code \definecolor{epiphanycrimson}{HTML}{7A2424} % deep crimson for warnings \hypersetup{ colorlinks=true, linkcolor=epiphanyteal, citecolor=epiphanyteal, urlcolor=epiphanygold, pdftitle={Epiphany --- Core Specification}, pdfauthor={The Epiphany Project}, pdfsubject={Core specification for the Epiphany music notation platform}, pdfkeywords={music notation, engraving, FOSS, Rust, microtonality, CRDT}, bookmarksnumbered=true, bookmarksopen=true } % --------------------------------------------------------------------------- % Typography: classical serif with refined sans for chapter titles % - TeX Gyre Pagella: Palatino-derived; warm, classical, highly legible % - TeX Gyre Heros: clean grotesque for sans elements % - TeX Gyre Cursor: monospace for code % --------------------------------------------------------------------------- \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} ] % Lining figures for tables (oldstyle for body, lining for technical) \newcommand{\tablenums}[1]{{\addfontfeatures{Numbers={Lining,Tabular}}#1}} % A display-weight version of Pagella for the title page \newfontfamily\titlefont{TeX Gyre Pagella}[ Numbers={OldStyle}, Ligatures={TeX, Common} ] % Small-caps helpers \newcommand{\sectionsc}[1]{{\addfontfeatures{Letters=SmallCaps}#1}} % --------------------------------------------------------------------------- % Section styling % --------------------------------------------------------------------------- \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 % --------------------------------------------------------------------------- \pagestyle{fancy} \fancyhf{} \renewcommand{\headrulewidth}{0pt} \renewcommand{\footrulewidth}{0pt} \fancyhead[L]{\small\scshape\color{epiphanyslate}Epiphany} \fancyhead[R]{\small\itshape\color{epiphanyslate}\leftmark} \fancyfoot[C]{\small\color{epiphanyslate}\thepage} % Subtle ornamental rule under the page header \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 % --------------------------------------------------------------------------- \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 % --------------------------------------------------------------------------- % Open question or unresolved design issue \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 } % Design rationale \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 } % Normative requirement \newtcolorbox{requirement}[1][]{ enhanced, breakable, colback=white, colframe=epiphanygold, fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Requirement}, 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 } % Non-goal: explicitly out of scope \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 } % Normative-language helpers (RFC 2119 style). MUST NOT and SHOULD NOT % are emitted as two adjacent textbf groups separated by a regular % control space; this prevents line breaks between the words while % still producing a standard ASCII space character in the PDF that % pdftotext extracts cleanly. The \nobreak prevents the line-break % the regular space would otherwise allow. \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}} % List spacing \setlist[itemize]{topsep=2pt, itemsep=3pt, parsep=0pt} \setlist[enumerate]{topsep=2pt, itemsep=3pt, parsep=0pt} \setlist[description]{topsep=2pt, itemsep=5pt, parsep=0pt} % Body text color \AtBeginDocument{\color{epiphanyink}} % --------------------------------------------------------------------------- % Document % --------------------------------------------------------------------------- \begin{document} % ----- Title page ----- \begin{titlepage} \thispagestyle{empty} \begin{tikzpicture}[remember picture, overlay] % Top ornamental rule \draw[epiphanygold, line width=1.2pt] ([yshift=-0.85in, xshift=1.15in]current page.north west) -- ([yshift=-0.85in, xshift=-1.15in]current page.north east); \draw[epiphanygold, line width=0.4pt] ([yshift=-0.93in, xshift=1.15in]current page.north west) -- ([yshift=-0.93in, xshift=-1.15in]current page.north east); % Bottom ornamental rule \draw[epiphanygold, line width=0.4pt] ([yshift=0.93in, xshift=1.15in]current page.south west) -- ([yshift=0.93in, xshift=-1.15in]current page.south east); \draw[epiphanygold, line width=1.2pt] ([yshift=0.85in, xshift=1.15in]current page.south west) -- ([yshift=0.85in, xshift=-1.15in]current page.south east); \end{tikzpicture} \centering \vspace*{0.7in} % Small superscript framing {\color{epiphanyslate}\scshape\fontsize{11pt}{13pt}\selectfont A music notation platform\par} \vspace{0.3in} % The main title --- Epiphany {\titlefont\fontsize{72pt}{76pt}\selectfont \color{epiphanyteal} Epiphany\par} \vspace{0.12in} % Subtle ornamental separator \begin{tikzpicture} \draw[epiphanygold, line width=0.5pt] (-1.3,0) -- (-0.3,0); \node at (0,0) {\color{epiphanygold}\large$\diamond$}; \draw[epiphanygold, line width=0.5pt] (0.3,0) -- (1.3,0); \end{tikzpicture} \vspace{0.2in} {\color{epiphanyink}\fontsize{18pt}{22pt}\selectfont \itshape Core Specification\par} \vspace{0.9in} % Tagline \begin{minipage}{0.78\textwidth} \centering \color{epiphanyink} \fontsize{12pt}{17pt}\selectfont A foundational specification for an open, Rust-native music notation platform: pitch and time primitives, the score graph, the layout intermediate representation, the file format, and the constraint-solver interface. \end{minipage} \vspace{0.7in} % Status banner \begin{tikzpicture} \node[ draw=epiphanygold, line width=0.5pt, fill=epiphanycream, inner sep=10pt, minimum width=3.5in, ] (status) { \color{epiphanyteal} \scshape\fontsize{10pt}{12pt}\selectfont Working Draft \quad$\cdot$\quad \today }; \end{tikzpicture} \vspace{0.5in} \begin{minipage}{0.72\textwidth} \centering \itshape\color{epiphanyslate} \footnotesize This specification defines the foundational layer of the Epiphany platform: the data model, engraving engine, and file format on which all higher-level features are built. The user interface, audio engine, plugin runtime, and platform integrations are layered above the core and are addressed in separate specifications. \end{minipage} \vfill \end{titlepage} % ----- Front matter ----- \pagenumbering{roman} \chapter*{Status of This Document} \addcontentsline{toc}{chapter}{Status of This Document} This draft is \textbf{structurally stabilized}. Substantive review passes through the core type model, graph invariants, concurrent reduction semantics, file-format atomicity, solver conformance, and the determinism contract have been completed. The architecture is no longer under active redesign; further revisions are expected to be additive (companion specifications, normative algorithm choices for the open canonical algorithms, extension registry contents) rather than structural. The remaining blockers to full conformance are companion specifications and the canonical algorithms identified in Appendix~\ref{app:determinism} Section~\ref{sec:det:open}: \begin{itemize} \item Several companion specifications referenced throughout the document have not yet been delivered: Appendix~\ref{app:deferred} catalogs them and distinguishes them from accidentally missing content. \item Two algorithms affect canonical state and have not yet received one of the dispositions in Appendix~\ref{app:determinism} Section~\ref{sec:det:open}: the tempo curve integration algorithm and the \texttt{wallclock\_to\_musical} root-finding algorithm. Cross-implementation byte equality of canonical score state \MUSTNOT{} be claimed for outputs derived from these algorithms until they are normatively specified or profile-declared (Appendix~\ref{app:determinism} Section~\ref{sec:det:layers}). The spelling pre-pass and the notational-decomposition algorithm received the profile-declared-by-versioned-identifier disposition in Pass~12 (Requirements~\ref{req:pitch:spelling-algorithm} and~\ref{req:time:decomposition-algorithm}). \item Extension registry catalogs are referenced by typed identifier but not enumerated here; they are delivered as separate, versioned registry documents. \end{itemize} What conformance can be established now: \begin{itemize} \item The core data model can be structurally implemented and tested against the invariants in Chapter~\ref{ch:graph} Section~\ref{sec:graph:invariants}. \item The operation framework (Chapter~\ref{ch:semops}) can be implemented end-to-end against the canonical reduction algorithm. \item The file format (Chapter~\ref{ch:format}) can be implemented atomically and crash-recoverably against the fixed prelude and superblock protocol. \item Solver implementations can be developed against the interface contract (Chapter~\ref{ch:solver}); reference-suite conformance becomes checkable when the Reference Suite companion is delivered. \end{itemize} The normative keywords \MUST, \MUSTNOT, \SHOULD, \SHOULDNOT, and \MAY{} are to be interpreted as described in RFC 2119 when they appear in all capital letters. \chapter*{Conventions} \addcontentsline{toc}{chapter}{Conventions} \begin{description} \item[Code samples] Rust syntax is used for type signatures, struct definitions, and algorithmic illustrations. Code samples are illustrative unless explicitly labeled normative. \item[Normative requirements] Statements containing \MUST, \MUSTNOT, \SHOULD, \SHOULDNOT, or \MAY{} are normative. Conforming implementations must respect them. \item[Non-normative content] Discussion, rationale, examples, and diagrams are non-normative unless explicitly stated. \item[Forward references] The specification builds bottom-up. Later chapters depend on earlier ones; forward references are minimized but occasionally unavoidable and are marked when they occur. \end{description} \tableofcontents \clearpage \chapter*{Reading Guide} \addcontentsline{toc}{chapter}{Reading Guide} This document is long. The following tables summarize the most load-bearing concepts and where they are defined. They are non-normative; the chapters they reference are authoritative. \section*{Score Graph Identity Hierarchy} The score graph is organized as a containment hierarchy of identified objects. Each level introduces a distinct concept of identity. \begin{longtable}{p{2.8cm} p{2.6cm} p{8.2cm}} \toprule \textbf{Level} & \textbf{Identifier} & \textbf{Role} \\ \midrule \endhead Score & --- & Document root. Carries metadata, instruments, global staves, staff groups, tuning context, tempo map, canvas. \\ Canvas & --- & Spatial root. Partitioned into regions. \\ Region & \texttt{RegionId} & Spatial-temporal container. Declares time model (metric/proportional/aleatoric) and content model (staff-based/free-graphic/hybrid). \\ Staff & \texttt{StaffId} & Global, abstract staff identity persisting across regions. Declared once at score level. \\ Staff instance & \texttt{StaffInstanceId} & Region-local manifestation of a staff. One \texttt{Staff} may have multiple instances across regions. \\ Voice & \texttt{VoiceId} & Polyphonic line within a staff instance. Carries \texttt{VoiceOrigin}: user-declared, imported, or system-promoted. \\ Event & \texttt{EventId} & Rhythmic atom: pitched, unpitched, rest, indeterminate, trajectory, graphic, cue. Lives in the score's event arena. \\ Identified pitch & \texttt{PitchId} & Stable identity for a pitch within an event. Enables spelling attachments, respelling, stable tie pairing through chord reordering. \\ \bottomrule \end{longtable} \section*{Canonical versus Non-Canonical} Many file-format and runtime objects exist in two forms. Canonical objects define the document; non-canonical objects accelerate access but never affect canonical state. \begin{longtable}{p{4.4cm} p{4.0cm} p{5.2cm}} \toprule \textbf{Object} & \textbf{Status} & \textbf{Notes} \\ \midrule \endhead Operation envelope set & Canonical & The replicated grow-only CRDT defining the document (Chapter~\ref{ch:semops}). \\ Operation envelope block & Canonical (storage) & Soft 1\,MiB blocks on disk (Chapter~\ref{ch:format}). \\ Operation index & Non-canonical & Acceleration of operation-id lookup. Rebuildable. \\ Canonical-base snapshot & Canonical & The manifest's \texttt{canonical\_base}: at most one active, with declared causal frontier and reduction algorithm version. Required for pruned documents. \\ Acceleration snapshot & Non-canonical & Listed in \texttt{acceleration\_snapshots}. Cache only. Discardable. \\ Manifest & Canonical (index) & Table of roots; itself a chunk; referenced from the active superblock. \\ Materialized score graph & Derivative & Deterministic reduction of the operation set under the active reduction algorithm. \\ Layout cache, glyph cache, render artifacts & Non-canonical & Always discardable (Section~\ref{sec:semops:caches}). \\ Conflict registry & Canonical & Stable, addressable, user-visible records of operations that could not apply cleanly. \\ \bottomrule \end{longtable} \section*{Operation Lifecycle} Every operation traverses four phases between user intent and its visible effect. \begin{longtable}{p{2.2cm} p{12.5cm}} \toprule \textbf{Phase} & \textbf{Description} \\ \midrule \endhead Prepare & Local UI command validates enough to construct a well-formed envelope. Advisory only; failure here means the UI rejects the user's action. Does not bind canonical semantics. \\ Commit & The envelope is added to the replicated operation set. After commit, the operation is part of canonical state and \MUSTNOT{} be discarded outside the pruning protocol. \\ Reduce & During materialization, the operation is processed by the canonical reduction algorithm against working state. Deterministic: every replica with the same operation set produces the same output. \\ Report & The reduction produces a deterministic \texttt{OperationEffect}: \texttt{Applied}, \texttt{AppliedWithRepair}, \texttt{Conflicted}, \texttt{TombstonedTarget}, or \texttt{NoOp}. Effects are visible graph facts. \\ \bottomrule \end{longtable} \section*{Determinism Layers} The specification distinguishes five layers of determinism. Conflating them is the most common source of impossible requirements. \begin{longtable}{p{3.8cm} p{10.0cm}} \toprule \textbf{Layer} & \textbf{Contract} \\ \midrule \endhead Canonical score determinism & Identical operation sets produce identical materialized score states across all conforming implementations, subject to all canonical algorithms having received the dispositions in Appendix~\ref{app:determinism}. \\ Canonical serialization & Same canonical state $\to$ same chunk hashes, same text projection, same canonical iteration order. \\ Layout determinism & Within one solver implementation at a fixed version: byte-equal output for identical input. Across implementations: reference-suite thresholds, not byte equal. \\ Conformance determinism & Reference-suite-based. Cross-implementation byte equality is not required for layout. \\ Non-canonical caches & May vary freely across runs, platforms, implementations. \MUSTNOT{} affect canonical state. \\ \bottomrule \end{longtable} \section*{Solver Tiers} Solver implementations declare a conformance tier. Tiers are orthogonal to file-format profiles (Chapter~\ref{ch:format}). \begin{longtable}{p{3.0cm} p{11.0cm}} \toprule \textbf{Tier} & \textbf{Obligations} \\ \midrule \endhead Minimal & Hard-constraint validity, within-implementation determinism, well-formed \texttt{SolveReport}, CMN + metric regions, Minimal reference-suite subset. Aesthetic quality may be mediocre. \\ Standard & Minimal $+$ Standard-tier thresholds on the full reference suite, Measure-local invalidation under incremental solving, all Standard constraint families. Professional engraving quality for common-practice notation. \\ Advanced & Standard $+$ proportional and aleatoric regions, microtonal accidentals, extension-contributed constraints, System-local invalidation under incremental solving. \\ \bottomrule \end{longtable} \section*{Chapter Map} \begin{longtable}{p{1.6cm} p{12.4cm}} \toprule \textbf{Ch.} & \textbf{Content} \\ \midrule \endhead \ref{ch:intro} & Scope, design principles, glossary of key terms used throughout. \\ \ref{ch:pitch} & Pitch as two intrinsic layers (scale position and acoustic), spelling subsystem with stacked accidentals, analytical layers. \\ \ref{ch:time} & Rational time, time anchors with \texttt{AnchorOffset}, three region time models, tempo map with explicit segment endpoints, sounding duration vs. notational decomposition. \\ \ref{ch:tuning} & Pitch spaces (including CMN and JI), tuning systems, score tuning context, accidental registries, glyph references. \\ \ref{ch:graph} & Score graph, event arena, cross-cutting structures, regions, staff identity (Staff vs. StaffInstance), polymeter via per-staff metric grids, voice origin, graph invariants. \\ \ref{ch:semops} & Operation framework, canonical reduction, DVV causal contexts, operation effects, tombstones, conflict records, transactions, re-anchoring, undo, LWW discipline. \\ \ref{ch:layout-ir} & The four-stage layout intermediate representation; provenance and incremental invalidation. \\ \ref{ch:format} & Fixed prelude, two superblock slots, atomic commit, content-addressed BLAKE3 chunks, operation-envelope blocks, snapshots as canonical bases, edit barriers, text projection. \\ \ref{ch:solver} & Solver interface, SolveReport, deterministic budgets, NormalizedMetric, three conformance tiers, reference-suite conformance, six invalidation scopes. \\ \ref{ch:perf} & Performance requirements: frame budgets, cold open, memory, audio-thread discipline, measurement. \\ \ref{ch:extension} & Consolidated catalog of extension points by chapter of origin. \\ \ref{app:deferred} & Intentionally deferred types: companion specifications, externally provided components, open canonical algorithms. \\ \ref{app:glossary} & Glossary of defined terms with chapter cross-references. \\ \ref{app:refs} & Bibliography and references. \\ \ref{app:determinism} & Determinism contract: the legal code for reproducibility. \\ \ref{app:bytes} & Canonical byte-layout reference: every ratified discriminant, derivation preimage, and primitive encoding in one place, for the Binary Format companion to import. \\ \ref{app:history} & Revision history. \\ \bottomrule \end{longtable} % ----- Main matter ----- \clearpage \pagenumbering{arabic} % =========================================================================== \chapter{Introduction and Scope} \label{ch:intro} \section{Purpose} This document specifies the core layer of \textit{Epiphany}, a music notation platform. The core comprises the abstract musical data model, the engraving engine that transforms that data into a typeset layout, and the on-disk file format that serializes both. Everything else built atop the platform---editing UI, audio engine, plugin runtime, collaboration, export pipelines---depends on this core and must conform to its interfaces. The name \textit{Epiphany} evokes both the original sense of the word---a manifestation made visible---and the experience of music itself, which strikes the listener and the creator alike. Music notation is, in a literal sense, the act of making the inaudible visible: a body of work this central to that task deserves a name of corresponding weight. \section{Scope} \textbf{In scope:} \begin{itemize} \item Pitch, time, and duration primitives. \item The score graph: the in-memory representation of musical content. \item The layout intermediate representation produced by the engraving engine. \item The file format: the canonical on-disk projection of the score graph. \item The constraint-solver interface consumed by the engraving engine. \item Extension points for non-Common-Music-Notation systems, including microtonal pitch spaces and graphic-notation regions. \end{itemize} \textbf{Out of scope for this document:} \begin{nongoal} The following are explicitly out of scope for this core specification. Each is addressed by a separate specification layered atop the core. \begin{itemize} \item User interface, input handling, and editing operations. \item Audio synthesis, playback, and plugin (VST3/AU/CLAP/LV2) hosting. \item The plugin runtime (WebAssembly capability-based extensions). \item Real-time collaboration transport and federation protocols. \item Import and export pipelines for foreign formats (MusicXML, MEI, LilyPond, MIDI, etc.), although the core defines the data sufficient to make lossless interchange feasible. \item Machine-learning components (transcription, OMR, engraving assistance). \item Platform-specific integrations (file pickers, sharing, accessibility APIs). \end{itemize} \end{nongoal} \section{Design Goals} \begin{description} \item[Correctness over convenience.] The data model must represent music accurately, even when this is harder than a convenient approximation. MIDI-style pitch numbers, for example, are inadequate and rejected; see Chapter~\ref{ch:pitch}. \item[Extensibility without bolt-ons.] Microtonality, graphic notation, historical notations, and future notational systems must be expressible within the core data model, not as escape hatches. \item[Determinism.] Given identical input, the engraving engine must produce identical output. The on-disk format must serialize deterministically. \item[Incrementality.] The data model and layout engine must support fine-grained incremental edits without full recomputation. \item[Reducibility.] All editing operations on the score graph must have well-defined reduction rules under canonical deterministic reduction (Chapter~\ref{ch:semops}), enabling collaboration and version control as natural consequences of the data model rather than bolt-on features. \item[Performance.] Layout and rendering of a 100-page orchestral score must remain interactive on commodity hardware. See Chapter~\ref{ch:perf} for budgets. \item[Privacy.] The core must operate entirely offline. No core functionality may depend on network access. \end{description} \section{Document Structure} The remainder of this document is organized bottom-up: \begin{enumerate} \item Chapter~\ref{ch:pitch} defines pitch: its layered structure, the spelling subsystem, scale positions, and acoustic realization. \item Chapter~\ref{ch:time} defines time and duration primitives. \item Chapter~\ref{ch:tuning} defines tuning systems, pitch spaces, and accidental registries. \item Chapter~\ref{ch:graph} defines the score graph: how primitives compose into staves, voices, regions, and the overall score structure, including the canvas/region model that accommodates non-CMN notations. \item Chapter~\ref{ch:semops} defines the semantic operations on the score graph: the canonical set of mutations and their reduction semantics under concurrent edits. \item Chapter~\ref{ch:layout-ir} defines the layout intermediate representation: the contract between the score graph (input) and the engraving engine (consumer). \item Chapter~\ref{ch:format} defines the on-disk file format, including serialization, chunking, the operation-envelope set and its deterministic reduction, and schema evolution. \item Chapter~\ref{ch:solver} defines the constraint-solver interface consumed by the engraving engine. \item Chapter~\ref{ch:perf} states performance requirements and measurement methodology. \item Chapter~\ref{ch:extension} describes the extension points by which the core supports notation grammars beyond Common Music Notation. \end{enumerate} % =========================================================================== \chapter{Pitch} \label{ch:pitch} This chapter specifies the pitch primitive: the data type representing a single sounding (or to-be-sounded) note's identity in the score graph. Pitch is the most consequential primitive in the core because nearly every higher-level operation---transposition, key analysis, accidental rendering, playback frequency, interchange, voice-leading analysis---is defined in terms of it. The definition is therefore deliberately verbose. \section{Design Principles} \label{sec:pitch:principles} \begin{description} \item[Separation of identity layers.] Pitch comprises three independent semantic layers: scale position (analytical identity), acoustic realization (sounding frequency), and spelling (notational appearance). Each is a first-class concern and \MUST{} be representable independently. Conflating them is a design error. \item[Spelling is attachment, not field.] A pitch carries its scale position and acoustic identity intrinsically. Spelling is attached externally through a separate indexed subsystem (see Section~\ref{sec:pitch:spelling}). A pitch may have zero, one, or multiple spelling attachments. \item[Grammar neutrality.] The pitch type \MUST{} support Common Music Notation as a fast path while remaining capable of representing pitches in arbitrary user-defined notation grammars. CMN is a privileged default, not a hardcoded ceiling. \item[Microtonality is not an extension.] Microtonal pitches are representable using the same primitive as 12-tone equal-tempered pitches. No separate type, no escape hatch. \item[Unpitched events are not pitches.] Unpitched percussion and similar events \MUSTNOT{} be represented using the pitch type. See Section~\ref{sec:pitch:not-pitch}. \end{description} \section{The \texttt{Pitch} Type} \label{sec:pitch:type} A pitch is a record of two intrinsic layers: \begin{lstlisting}[language=Rust] /// A pitch's intrinsic identity. Spellings are attached externally; /// see Section~\ref{sec:pitch:spelling}. pub struct Pitch { /// The analytical identity within a pitch space. Determines /// transposition, key analysis, and Roman-numeral behavior. pub scale_position: ScalePosition, /// The acoustic identity: what frequency this pitch produces when /// sounded, expressed relative to a tuning system. pub acoustic: AcousticPitch, } \end{lstlisting} The two layers are independent. A given scale position can be acoustically realized in many ways depending on the active tuning system; a given acoustic realization can correspond to many scale positions in different analytical contexts. The pitch carries both; neither is derivable from the other in general. \begin{rationale} Storing both layers, rather than deriving one from the other, costs a small number of bytes per pitch and saves a large amount of complexity downstream. Derivation requires the renderer, the analyzer, the transcriber, and the importer to each agree on a derivation procedure; storage makes the pitch self-describing. \end{rationale} \begin{lstlisting}[language=Rust] /// A closed range of pitches, used for advisory declarations: an /// instrument's playable compass and an indeterminate event's pitch hint. /// Both bounds are full pitches. pub struct PitchRange { pub lowest: Pitch, pub highest: Pitch, } \end{lstlisting} A \texttt{PitchRange} is \emph{advisory}. Whether a given pitch lies within it is decidable only when the pitch and both bounds share a pitch space that defines an order (the common CMN case); cross-space or partially-ordered comparisons are treated as ``not out of range'' --- a sound-but-incomplete check, as with region time overlap. A range is well-formed when \texttt{lowest} does not sort above \texttt{highest} in a common ordered space. A solver \MAY{} flag an out-of-range pitch but \MUSTNOT{} treat the range as a hard constraint. The type is defined with schema major~1, closing the gap in which \texttt{IndeterminacyHints} and \texttt{Instrument} named a \texttt{PitchRange} that no section defined. \section{Scale Position} \label{sec:pitch:scale-position} A scale position locates the pitch within a \emph{pitch space}. A pitch space is the analytical universe in which scale-degree and interval relationships are defined. \begin{lstlisting}[language=Rust] pub struct ScalePosition { /// The pitch space this position is defined within. References an /// entry in the score's pitch-space registry. pub space: PitchSpaceId, /// The position within that space. Interpretation is space-defined. pub position: PitchSpacePosition, } \end{lstlisting} The \texttt{PitchSpacePosition} type is a tagged union supporting the common cases with a fast representation, plus a registry-id escape hatch for arbitrary grammars: \begin{lstlisting}[language=Rust] pub enum PitchSpacePosition { /// Common Music Notation: a diatonic nominal plus chromatic /// alteration plus octave. The fast path for tonal Western music. Cmn { nominal: CmnNominal, // C, D, E, F, G, A, B alteration: i8, // -2..=+2 in semitones octave: i8, // scientific pitch notation }, /// N-tone integer position. Used for 12-tone serial music, EDO /// systems, and any space where pitches are indexed as integers. Integer { space_size: u16, // e.g., 12, 19, 31, 53 index: i32, // absolute index; octave is implicit }, /// Just-intonation lattice position. Used for JI pitch spaces. /// Each component is the integer power of the corresponding prime /// in the space's prime basis. The pitch space declares the basis /// ordering; by convention prime 2 is first, so the leading /// component carries octave information. JiVector { components: Vec, }, /// A registered position whose interpretation is defined by the /// pitch space's grammar plugin. Used for maqamat, gamelan, /// historical modes, and other non-lattice, non-CMN grammars. Registered(PositionRegistryId), } \end{lstlisting} \begin{requirement} For \texttt{PitchSpacePosition::JiVector}: \begin{itemize} \item \texttt{components.len()} \MUST{} equal the number of primes in the enclosing pitch space's declared prime basis. \item The basis ordering is established by the pitch space; the built-in JI pitch spaces (\texttt{ji-5limit}, \texttt{ji-7limit}, \texttt{ji-11limit}) order primes ascending starting with 2. \item The vector $[a_0, a_1, a_2, \ldots]$ denotes the ratio $p_0^{a_0} \cdot p_1^{a_1} \cdot p_2^{a_2} \cdots$ where $p_i$ is the $i$-th prime in the basis. With the conventional basis $\{2, 3, 5, \ldots\}$, the first component holds octave information. \item Pitch spaces \MAY{} declare octave-reduced equivalence, in which case the first component is normalized to a canonical range. Without such a declaration, full register is preserved. \end{itemize} \end{requirement} \begin{requirement} Every score \MUST{} define at least one pitch space. The default pitch space for CMN scores is \texttt{cmn-12}, which is diatonic-over-chromatic in structure (seven diatonic nominals A--G over twelve chromatic positions), anchored to the score's reference pitch as resolved through the score tuning context (Chapter~\ref{ch:tuning}). Scores \MAY{} define additional pitch spaces; pitches in different spaces cannot be directly compared and operations between them \MUST{} go through an explicit space-conversion mechanism. \end{requirement} \subsection{Octave Convention} \label{sec:pitch:octave} For the \texttt{Cmn} variant, the octave field uses Scientific Pitch Notation: middle C is C4. This convention is normative throughout the specification. \begin{requirement} The \texttt{octave} field of \texttt{PitchSpacePosition::Cmn} \MUST{} follow Scientific Pitch Notation. Middle C is C4. The lowest A on a standard piano is A0. C-1 is one octave below the lowest C on a standard piano. \end{requirement} \begin{rationale} Scientific Pitch Notation is the dominant modern convention in English-language music theory and notation software. MIDI octave numbering, where middle C is sometimes C3 and sometimes C5 depending on vendor, is rejected as a source of foot-guns. Helmholtz notation is expressible at the display layer; the data model commits to Scientific. \end{rationale} \subsection{The \texttt{CmnNominal} Type} \begin{lstlisting}[language=Rust] #[repr(u8)] pub enum CmnNominal { C = 0, D = 1, E = 2, F = 3, G = 4, A = 5, B = 6 } \end{lstlisting} The discriminants are normative: they define the diatonic step ordering used by transposition algorithms. \section{Acoustic Realization} \label{sec:pitch:acoustic} The acoustic layer specifies the sounding frequency. It does not carry a frequency directly; instead it references a tuning system that, given the pitch's scale position, resolves to a frequency. \begin{lstlisting}[language=Rust] pub struct AcousticPitch { /// The tuning system governing this pitch's frequency. /// May be inherited; see Section~\ref{sec:pitch:tuning-inherit}. pub tuning: TuningReference, /// How the tuning system resolves to a frequency. pub realization: AcousticRealization, } pub enum AcousticRealization { /// Resolve through the tuning system using the scale position alone. /// This is the default case for ordinary CMN. Implicit, /// An explicit offset in cents from what the tuning system would /// otherwise produce. For inflections, expressive retuning, or /// just-intonation adjustments within a tempered piece. CentsOffset(f64), /// An explicit absolute frequency in Hertz, overriding the tuning /// system. For spectral music or fixed-frequency electronic parts. AbsoluteHz(f64), } \end{lstlisting} \subsection{Cents as the Offset Unit} Cents (one cent equals one hundredth of an equal-tempered semitone) are the normative offset unit. \texttt{f64} provides far more precision than the ear can resolve in any context. \subsection{Tuning Reference and Inheritance} \label{sec:pitch:tuning-inherit} A \texttt{TuningReference} is either an explicit tuning-system identifier or an inheritance marker: \begin{lstlisting}[language=Rust] pub enum TuningReference { /// Inherit the tuning system from the enclosing scope. Inherit, /// Explicitly named tuning system. Explicit(TuningSystemId), } \end{lstlisting} Resolution proceeds by walking outward: pitch, then voice, then staff, then region, then score. The score's tuning system \MUST{} be explicit; \texttt{Inherit} at the score level is a malformed document. Tuning systems and their resolution rules are defined in detail in Chapter~\ref{ch:tuning}. \subsection{Reference Pitch} \label{sec:pitch:reference} A pitch's \texttt{TuningReference} resolves, through the hierarchical walk, to a tuning system. The \emph{reference pitch} (the position and frequency that anchor that tuning system's structure to absolute Hertz) is resolved separately through the same hierarchical walk, and is a property of the score's tuning context, not of the tuning system itself. The pitch type does not own or imply a reference frequency. Two scores using the same tuning system (12-TET) but different references (A4 = 440\,Hz versus A4 = 415\,Hz) share their tuning structure entirely; only their reference pitch differs. The full reference-pitch model, including the \texttt{ReferencePitch} type and the score tuning context that owns it, is specified in Chapter~\ref{ch:tuning} (Section~\ref{sec:tuning:reference}). \section{Spelling} \label{sec:pitch:spelling} Spelling determines what the performer sees on the page for a given pitch: the staff position, the accidental glyph (if any), and any ancillary notational marks (parenthesized accidentals, courtesy accidentals, microtonal accidentals). Spelling \MUSTNOT{} be a field on the pitch type. Spellings are stored externally, indexed by pitch identifier, and managed by the spelling subsystem defined in this section. \begin{rationale} Spelling as a field on pitch causes destructive overwrites on respelling, prevents partial spelling (where only some pitches have user-chosen spellings and the rest are inferred), prevents multiple spellings per pitch (analytical layers), and prevents provenance tracking. Externalizing spelling resolves all four. \end{rationale} \subsection{The Spelling Attachment} \begin{lstlisting}[language=Rust] pub struct SpellingAttachment { /// What this attachment applies to: a single pitch or a scope. pub scope: SpellingScope, /// The directive: explicit spelling or a rule for inference. pub directive: SpellingDirective, /// Provenance of this attachment. pub source: SpellingSource, /// Tie-break priority among attachments. Higher wins. See /// Section~\ref{sec:pitch:precedence} for the configurable /// precedence model. pub priority: i32, /// Optional analytical layer. None means the engraved layer; /// Some(id) means an alternative analytical layer that does not /// affect the engraved score. pub layer: Option, } pub enum SpellingScope { /// Applies to one specific pitch. Pitch(PitchId), /// Applies to all pitches matching the selector within a time range. Range { start: TimeAnchor, end: TimeAnchor, voices: VoiceSelector, }, } pub enum SpellingDirective { /// An explicit spelling for a single pitch. Only valid with /// SpellingScope::Pitch. Explicit(PitchSpelling), /// A rule for inferring spellings within a scope. Rule(SpellingRule), } pub struct PitchSpelling { /// The nominal: the staff position. For CMN, this is one of A-G. /// For other grammars, an identifier into the grammar's nominal /// registry. pub nominal: SpellingNominal, /// Stack of accidentals applied to the nominal, in stacking /// order: innermost (closest to the notehead) first. /// Empty vector means no accidental glyph is drawn, which does /// not necessarily mean "natural"; see /// Section~\ref{sec:pitch:absent-accidental}. pub accidentals: Vec, /// The octave designation. pub octave: i8, /// Optional rendering hints: parenthesized, cautionary, editorial, /// small-print, etc. Non-normative for sounding pitch. pub render_hints: SpellingRenderHints, } pub enum SpellingNominal { /// CMN nominals: the fast path. Cmn(CmnNominal), /// Integer nominals for N-tone systems. Integer(i32), /// Registered nominals for grammar-specific systems. Registered(NominalRegistryId), } \end{lstlisting} \subsubsection{Accidental Stack Semantics} \label{sec:pitch:accidental-stack} \begin{requirement} The \texttt{accidentals} vector \MUST{} be interpreted as follows: \begin{itemize} \item The stored order is normative for engraving: index 0 is the innermost glyph (closest to the notehead), with subsequent indices placed further outward. \item For built-in additive accidental systems, the pitch-space modification contributed by the stack is the sum of the modifications of its members, and is independent of stored order. Reordering the stack changes engraving appearance but \MUSTNOT{} change sounding pitch. \item Custom accidental systems \MAY{} declare non-additive combination semantics only through their registered \texttt{AccidentalCombination} (Section~\ref{sec:tuning:accidentals}). Otherwise, accidentals are treated as commutative under their pitch effect. \item Two accidentals with the same \texttt{AccidentalId} in one stack constitute a malformed spelling, unless the accidental's registered combination semantics explicitly permits repetition. The conventional double-sharp and double-flat are represented as single accidental definitions, not as repeated singles. \item An empty \texttt{accidentals} vector means no glyph is drawn; it is distinct from a vector containing only a natural sign (which explicitly cancels prior alterations and is drawn). \end{itemize} \end{requirement} \subsection{Spelling Sources} Every spelling attachment carries provenance: \begin{lstlisting}[language=Rust] pub enum SpellingSource { /// The user explicitly chose this spelling. UserChosen, /// Inferred by the spelling pre-pass from key signature and /// context. Lowest default precedence. The pre-pass does not /// store attachments (its output is a derived annotation); /// this variant ranks that output in the precedence order. Inferred, /// Imported from a foreign format. Imported { format: ForeignFormatId }, /// Propagated from a transposition or other edit operation. Propagated { from: PitchId }, /// An analytical spelling on a non-engraved layer. Analytical, } \end{lstlisting} \begin{requirement} Editing operations that respell a pitch \MUST{} produce attachments with source \texttt{UserChosen}. Editing operations that move or transpose a pitch \MUST{} produce attachments with source \texttt{Propagated}. Foreign-format importers \MUST{} produce attachments with source \texttt{Imported}. The spelling pre-pass (Section~\ref{sec:pitch:prepass}) \MUSTNOT{} mint attachments: its inferred spellings are derived annotations reported with \texttt{Inferred} provenance, promoted to a stored \texttt{UserChosen} attachment only by an explicit editing operation. \end{requirement} \subsection{Configurable Precedence} \label{sec:pitch:precedence} When multiple attachments target the same pitch on the same analysis layer, the renderer resolves the conflict by precedence. The default precedence is: \begin{enumerate} \item \texttt{UserChosen} (highest) \item \texttt{Imported} \item \texttt{Propagated} \item \texttt{Inferred} (lowest) \end{enumerate} \begin{requirement} Every score \MUST{} carry a \texttt{SpellingPrecedence} configuration. The configuration \MUST{} assign a total ordering over the variants of \texttt{SpellingSource}. The configuration \MAY{} differ from the default; for example, a workflow that treats MusicXML imports as authoritative may rank \texttt{Imported} above \texttt{UserChosen}. Conflicts are broken first by precedence, then by the \texttt{priority} field of the attachment (higher wins), then by canonical attachment order (the attachment appearing earliest in the score's canonical serialization wins). Attachments carry no creation timestamp: a timestamp tie-break would hang resolution on non-canonical state. \end{requirement} \subsection{The Spelling Pre-Pass} \label{sec:pitch:prepass} When a score is materialized (on load, after an edit, or ahead of rendering), the spelling pre-pass computes an inferred spelling for every spelling-eligible pitch. Its output is a \emph{canonical derived annotation}: a deterministic function of the materialized score graph, the active profile, and the versioned \texttt{SpellingAlgorithmId}, recomputed on materialization and never stored as graph state or serialized into canonical chunks. Where a stored attachment targets the pitch, the resolved annotation reports that attachment's spelling and provenance per the precedence configuration (Section~\ref{sec:pitch:precedence}); the pre-pass supplies the \texttt{Inferred}-provenance spelling that applies when no attachment outranks it. The pre-pass \MUST{} be deterministic: given the same materialized graph, precedence configuration, and algorithm version, it \MUST{} produce identical annotations. The pre-pass operates as follows: \begin{enumerate} \item Collect all pitches in the score, partitioned by voice and measure. \item For each partition, in time order, resolve each pitch's spelling using the applicable context inputs (which of these a given algorithm version consumes is part of its versioned definition; see Requirement~\ref{req:pitch:spelling-algorithm}): \begin{enumerate} \item The active key signature for the staff. \item The accidental context of prior pitches in the same measure and voice. \item The melodic context (preceding interval). \item The harmonic context (concurrent pitches in other voices). \item Any active scope-level \texttt{Rule} attachments. \end{enumerate} \item Report an \texttt{Inferred}-provenance derived annotation for each pitch. \end{enumerate} Because derived annotations are not stored, a change to the algorithm version deterministically invalidates them without any state migration. Implementations \MAY{} cache derived annotations and \MAY{} recompute incrementally (re-running only the affected measure and voice, and forward through measures whose context is altered, until the context stabilizes); a cache \MUST{} be invalidated when the derivation key --- graph, profile, algorithm version --- changes, and neither caching nor incrementality may be observable in the annotations produced. \begin{rationale} Deriving inferred spellings---rather than storing them as attachments---keeps algorithm output out of canonical state: two replicas at the same (graph, profile, algorithm version) agree on annotations byte-for-byte without ever exchanging them, an algorithm upgrade cannot strand stale inferred state, and serialization never churns on re-inference. Inferred spellings remain inspectable in the UI and editable by promotion: accepting one mints a \texttt{UserChosen} attachment through an editing operation, which then outranks the pre-pass. \end{rationale} \begin{requirement} \label{req:pitch:spelling-algorithm} \textbf{Spelling algorithm disposition (ratified Pass 12).} The spelling pre-pass receives the \emph{profile-declared by versioned identifier} disposition of Appendix~\ref{app:determinism} Section~\ref{sec:det:open}. The reserved identifier \texttt{SpellingAlgorithmId} \texttt{"default"} denotes, at version~1, a Temperley-style line-of-fifths preference algorithm: each partition's tonal context is estimated as a centre of gravity on the line of fifths from the accidental, melodic, and harmonic context, and each pitch takes the enharmonic spelling nearest that centre. The ascending-runs-take- sharps / descending-runs-take-flats convention participates \emph{only as a tiebreak} within the centre-of-gravity rule; an isolated chromatic run with no tonal context may therefore receive the enharmonic the convention alone would not pick, and a voice-leading refinement is a future algorithm version, not a deviation. Which of the context inputs enumerated above an algorithm version consumes is part of its versioned definition: \texttt{"default"} version~1 does \emph{not} consult declared key signatures (a key-aware refinement is a future version) and is region-time-model-independent --- pitches in aleatoric and proportional regions are spelled by the same rule as metric ones, and no region-specific spelling pass exists. A profile requesting any other identifier \MUST{} error; implementations \MUSTNOT{} silently substitute a different algorithm. \end{requirement} \begin{requirement} \label{req:pitch:authored-uninferred} \textbf{Authored annotations for inference-ineligible targets (ratified Pass 12).} A stored spelling attachment whose target the pre-pass produces \emph{no} inferred spelling for (an inference-ineligible pitch) \MUST{} still surface in the resolved derived annotations: the winning attachment under the precedence configuration (Section~\ref{sec:pitch:precedence}) is reported with its authored provenance, exactly as it would have been had it outranked an inferred spelling. An authored attachment is precisely how a user notates what the algorithm cannot infer; the derived-annotation surface \MUSTNOT{} render it invisible. Annotation taxonomy counts these authored-only resolutions distinctly from authored-over-inferred overrides. The same rule applies to the decomposition pre-pass (Section~\ref{sec:time:notrhythm}). \end{requirement} \subsection{Absent Accidentals} \label{sec:pitch:absent-accidental} A \texttt{PitchSpelling} with an empty \texttt{accidentals} vector means no accidental glyph is drawn at this position. This is distinct from a vector containing an explicit natural sign. The distinction matters in two cases: \begin{itemize} \item A pitch on a line affected by the key signature carries no accidental and sounds the key-signature pitch; drawing a natural would be a respelling. \item A pitch that follows an in-measure accidental on the same line and octave inherits that accidental; an empty vector means inheritance applies, while a vector containing a natural cancels the inheritance. \end{itemize} \begin{requirement} The renderer \MUST{} distinguish between an empty \texttt{accidentals} vector (no glyph) and a vector containing only a natural (explicit natural glyph) when materializing a spelling. Promotion from empty to natural-only, or vice versa, is a respelling operation. \end{requirement} \subsection{Analytical Layers} \label{sec:pitch:layers} A pitch may carry multiple spelling attachments on different analytical layers. The engraved layer (\texttt{layer: None}) governs what the performer sees. Other layers (\texttt{layer: Some(AnalysisLayerId)}) record alternative spellings used by analytical views, theory tools, or pedagogical overlays. The \texttt{AnalysisLayer} type itself is defined in Chapter~\ref{ch:graph} (Section~\ref{sec:graph:layers}); a layer identifier here refers to that authoritative definition. \begin{requirement} Resolution of a pitch's spelling for a given view \MUST{} consider only attachments whose \texttt{layer} matches the view's active layer. The engraved view's active layer is \texttt{None}. Analytical views may set an explicit layer; if no attachment on that layer matches, the view \MUST{} fall back to the engraved layer. \end{requirement} \section{Pitch Identifiers} \label{sec:pitch:ids} Every pitch in the score graph carries a stable identifier: \begin{lstlisting}[language=Rust] pub struct PitchId(pub u128); \end{lstlisting} \begin{requirement} Pitch identifiers \MUST{} be stable across edits: a pitch's identifier is assigned at creation and never reassigned. Pitch identifiers \MUST{} be unique within a score. Pitch identifiers \MUST{} be collision-resistant across distributed editing scenarios; the recommended generation strategy is a 128-bit value combining a per-replica identifier and a monotonic counter, with the exact format defined in Chapter~\ref{ch:semops}. \end{requirement} \section{What Pitch Is Not} \label{sec:pitch:not-pitch} Several musical phenomena resemble pitches but are not represented by the pitch type: \begin{description} \item[Unpitched events.] Snare drum hits, claves, cymbal strokes, and other unpitched percussion are sounding events with staff positions but no pitched identity. They are represented by a separate \texttt{UnpitchedEvent} type (see Chapter~\ref{ch:graph}). They have no scale position and no acoustic pitch in the sense defined here. \item[Rests.] A rest is a duration without a sounding event. Rests are represented by a separate \texttt{Rest} type. They carry timing and voice information but no pitch. \item[Indeterminate-pitch events.] Events whose pitch is deliberately indeterminate (text instructions like ``highest possible note,'' aleatoric pitch material, certain graphic-notation events) are represented by an \texttt{IndeterminatePitchEvent} type, which may carry hints (range, contour, density) but no concrete pitch. \item[Pitch trajectories.] Glissandi, portamenti, and continuous pitch bends are not single pitches but trajectories between or around pitches. They are represented as relationships between pitched events plus a trajectory shape, defined in Chapter~\ref{ch:graph}. \end{description} \begin{rationale} Forcing every sounding event into a pitch-shaped hole creates invariant-breaking edge cases throughout the codebase. Sibling types under a common \texttt{SoundingEvent} (or similar) abstraction keep each type coherent and let operations branch cleanly. \end{rationale} \section{Equality and Comparison} \label{sec:pitch:eq} Two pitches are \emph{structurally equal} if and only if their scale positions and acoustic realizations are equal field-by-field. Structural equality is the default \texttt{Eq} implementation. Three additional equivalence relations are defined: \begin{description} \item[Sounding equivalence.] Two pitches are sounding-equivalent if they resolve to the same frequency under their respective tuning systems. Computed, not derived from the type. \item[Scale-position equivalence.] Two pitches are scale-position- equivalent if their \texttt{ScalePosition} fields are equal, ignoring acoustic realization. \item[Enharmonic equivalence.] Two pitches are enharmonically equivalent if they are sounding-equivalent under 12-tone equal temperament, regardless of their actual tuning system. Used for foreign-format export and certain analytical operations. \end{description} \begin{requirement} Implementations \MUST{} provide functions for each of the three computed equivalences. Implementations \MUSTNOT{} conflate structural equality with sounding or enharmonic equivalence. \end{requirement} \section{Forward References} \begin{itemize} \item Tuning systems, pitch spaces, and accidental registries are defined in Chapter~\ref{ch:tuning}. \item The score graph, including how pitches are embedded in events, voices, and staves, is defined in Chapter~\ref{ch:graph}. \item \texttt{TimeAnchor}, \texttt{VoiceSelector}, \texttt{PitchId} generation, and the reduction rules governing spelling attachments under concurrent edits are defined in Chapter~\ref{ch:semops}. \end{itemize} % =========================================================================== \chapter{Time and Duration} \label{ch:time} This chapter specifies the time and duration primitives: how positions and durations are represented, how time signatures and tempo maps are modelled, how notational rhythm is decomposed from sounding duration, how tuplets and other groupings work, and how the core supports metric, proportional, and aleatoric time models simultaneously. \section{Design Principles} \label{sec:time:principles} \begin{description} \item[Exact rational arithmetic.] Musical positions and durations \MUST{} be exact rationals. Floating-point time is rejected as a source of accumulated error; integer-tick time is rejected as incompatible with arbitrary tuplet nesting. Exactness is non-negotiable. \item[Two clocks.] Musical time (measured in whole-note rationals) and wall-clock time (measured in fixed-point seconds) are distinct primitives. Conversion between them is a pure function of the tempo map. \item[Position and duration are distinct types.] A position and a duration share an underlying representation but are not interchangeable. The algebra (position $+$ duration $\rightarrow$ position; position $-$ position $\rightarrow$ duration; position $+$ position is undefined) is enforced at the type level. \item[Sounding duration is data; notational rhythm is derived.] A note's sounding duration is intrinsic; its notated decomposition (notehead values, ties, dots, tuplet membership) is produced by a deterministic pre-pass analogous to the spelling pre-pass and is overridable per-event by an authored attachment. \item[Multiple time models coexist.] A score \MUST{} support metric, proportional, and aleatoric regions, possibly concurrent on different staves. The region's declared time model determines the interpretation of positions within it. \item[Time anchors, not absolute positions.] Stored references to points in time \MUST{} anchor to identified objects (events, measures, regions) plus offsets. Absolute positions \MUSTNOT{} be stored where they could shift under edits. \end{description} \section{The Rational Time Type} \label{sec:time:rational} The fundamental time primitive is an exact rational number. The specification states the contract; implementations are free to choose representations meeting it. \begin{requirement} The rational time type \MUST{} behave as an exact rational with at least 32-bit numerator and 32-bit denominator range in normalized form. Operations \MUST{} produce exact results; on representation overflow, the implementation \MUST{} either widen to an exact unbounded representation or return an error. Silent loss of precision is forbidden. Implementations \MAY{} use any representation that meets this contract, including packed inline-or-heap-promoted designs. \end{requirement} \subsection{Recommended Implementation: Inline-or-Promoted} \label{sec:time:impl} The recommended implementation packs the common case (denominators under $2^{32}$) into 8 bytes and promotes to a heap-allocated arbitrary-precision rational on overflow. This is normative for conformance only inasmuch as the observable behavior must match; it is illustrative as a reference design. \begin{lstlisting}[language=Rust] /// A musical time value: position or duration. Exact rational. pub enum RationalTime { /// Inline case: fits in 8 bytes. Small(SmallRational), /// Promoted case: heap-allocated arbitrary-precision rational. /// Used only when arithmetic overflows the inline range. Large(Arc), } /// The inline rational: numerator i32, denominator NonZeroU32. /// Always normalized: gcd(|numerator|, denominator) == 1. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SmallRational { numerator: i32, denominator: NonZeroU32, } \end{lstlisting} \begin{requirement} All instances of the small rational \MUST{} be normalized: the greatest common divisor of the absolute value of the numerator and the denominator \MUST{} equal one, and the denominator \MUST{} be strictly positive. The constructor \MUST{} enforce normalization. \end{requirement} \subsection{Promotion and Demotion} Arithmetic operations on two small rationals are performed in widened intermediate types ($i64$ numerator, $u64$ denominator) and the normalized result is examined. If the result fits in the small representation, it is returned as \texttt{Small}; otherwise it is returned as \texttt{Large}. \begin{requirement} Arithmetic that exceeds the small representation's range \MUST{} silently promote to the large representation. The user of the type \MUST{} observe no behavioral difference between small-only and small-or-large arithmetic; the contract is exact arithmetic on exact rationals. Implementations \MAY{} demote a \texttt{Large} value back to \texttt{Small} when its normalized representation fits, but \MUSTNOT{} make demotion observable (e.g., by changing equality or hash behavior across the variant boundary). \end{requirement} \subsection{Equality and Ordering} Two rational time values are equal if and only if their normalized representations represent the same rational number. Equality between \texttt{Small} and \texttt{Large} variants \MUST{} compare the underlying values, not the variants. Ordering is the standard rational ordering. Comparison between rationals in the small representation is performed by cross-multiplication in widened intermediate types to avoid overflow. \subsection{Units} The unit of musical time is the \emph{whole note}. A quarter note has duration $\frac{1}{4}$. A triplet eighth has duration $\frac{1}{12}$. A quintuplet sixteenth inside a duplet half (an unusual but legal construct) has duration $\frac{1}{2} \cdot \frac{1}{2} \cdot \frac{1}{5} = \frac{1}{20}$. \begin{requirement} The whole note \MUST{} be the unit of musical time. The duration of a whole note is the rational $\frac{1}{1}$. All other durations are expressed as rationals relative to the whole note. \end{requirement} \begin{rationale} The whole note is the only unit under which durations form a multiplicative algebra with $1$ as identity. Quarter-note units would make ``the duration of a whole note'' equal to $4$, which is awkward; PPQ-tick units would force a choice of resolution. The whole-note unit also matches the etymology of note-value names: a half note is half of a whole note. \end{rationale} \section{Position and Duration as Distinct Types} \label{sec:time:newtypes} The rational time representation is shared between two distinct types: \texttt{MusicalPosition} and \texttt{MusicalDuration}. Both wrap a \texttt{RationalTime}. Their distinction is enforced at the type level to prevent algebraic errors. \begin{lstlisting}[language=Rust] /// A point in musical time, relative to the origin of a time region. #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct MusicalPosition(RationalTime); /// A span of musical time. #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct MusicalDuration(RationalTime); impl Add for MusicalPosition { type Output = MusicalPosition; // ... } impl Add for MusicalDuration { type Output = MusicalDuration; // ... } impl Sub for MusicalPosition { type Output = MusicalDuration; // ... } // MusicalPosition + MusicalPosition is intentionally not implemented. \end{lstlisting} \begin{requirement} Implementations \MUST{} provide distinct types for position and duration. The type system \MUST{} prevent adding two positions. The difference of two positions \MUST{} yield a duration; the sum of a position and a duration \MUST{} yield a position. \end{requirement} \begin{rationale} The illustrative Rust signatures derive \texttt{Clone}, not \texttt{Copy}, because \texttt{RationalTime} contains an \texttt{Arc} variant that cannot satisfy \texttt{Copy}. Implementations \MAY{} provide \texttt{Copy} semantics through a compact inline-or-pointer-tagged representation whose \texttt{Large} case is a non-owning interior pointer rather than an \texttt{Arc}; such implementations satisfy the contract here. Conformance does not require \texttt{MusicalPosition} or \texttt{MusicalDuration} to be \texttt{Copy}. \end{rationale} \section{Wall-Clock Time} \label{sec:time:wallclock} Wall-clock time is the time measured by the playback engine, by SMPTE timecode, and by external audio and video sources. It is distinct from musical time and is represented as a fixed-point integer count of a sub-second unit. \begin{lstlisting}[language=Rust] /// A point in wall-clock time, in nanoseconds from a region origin. /// 64-bit signed: range +/- ~292 years. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct WallClockTime(i64); // nanoseconds #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct WallClockDuration(i64); // nanoseconds \end{lstlisting} \begin{requirement} Wall-clock time \MUST{} be a fixed-point integer count of nanoseconds. Floating-point wall-clock time is forbidden in stored data. Implementations \MAY{} convert to floating-point for transient computation provided no stored or serialized value loses precision. \end{requirement} \begin{rationale} Nanosecond resolution provides over four orders of magnitude more precision than the human ear can resolve and aligns with the resolution conventions of professional audio interfaces and SMPTE-derived clocks. The 292-year range is sufficient for any conceivable musical work. \end{rationale} \subsection{Wall-Clock and Musical Time Coexist} A score event may be anchored in either musical or wall-clock time. Musical-time anchors are the default for ordinary notation; wall-clock anchors are used for film hit points, audio reference markers, fixed electronic parts in scores with live ensemble, and similar sync-critical content. Both anchor types may coexist in the same score, possibly on the same staff. Conversion between musical and wall-clock time is performed through the tempo map (Section~\ref{sec:time:tempomap}). \section{Time Anchors} \label{sec:time:anchors} Stored references to points in time \MUST{} be expressed as \emph{time anchors}: references to identified objects plus offsets. Absolute positions \MUSTNOT{} be stored where they could be invalidated by edits to the score. \begin{lstlisting}[language=Rust] pub enum TimeAnchor { /// Anchored to a specific event. Survives edits that do not /// delete the event. Event { id: EventId, offset: AnchorOffset }, /// Anchored to a measure boundary. Survives measure reordering /// (the anchor follows the measure). Measure { id: MeasureId, position: MeasurePosition, offset: AnchorOffset, }, /// Anchored to the start or end of a region. Region { id: RegionId, edge: RegionEdge, offset: AnchorOffset }, /// Anchored to absolute wall-clock time within the score. /// Used for film and audio sync. WallClock { time: WallClockTime }, } /// An offset applied to an anchor target. The variant kind is /// constrained by the target's enclosing region's time model. pub enum AnchorOffset { /// Offset in musical time. Valid for targets in metric regions /// and for aleatoric regions whose anchoring discipline /// admits musical coordinates. Musical(MusicalDuration), /// Offset in wall-clock time. Valid for targets in proportional /// regions and for aleatoric regions whose anchoring discipline /// admits wall-clock coordinates. WallClock(WallClockDuration), /// No offset; the anchor refers to the target's reference point /// exactly. Valid in any region. Zero, } pub enum MeasurePosition { Start, End } pub enum RegionEdge { Start, End } \end{lstlisting} \begin{requirement} Stored \emph{references to external time points} (cross-cutting endpoints, marker locations, attachment anchors, anything pointing at an object outside the referencing object's own voice and region) \MUST{} use the \texttt{TimeAnchor} type. Absolute musical positions \MUSTNOT{} appear as such references in stored data: a reference to ``measure 47, beat 3'' that does not survive a measure insertion in measure 12 is a broken reference. An event's own position within its owning voice and region \MAY{} be stored as an \texttt{EventPosition} (carrying a \texttt{MusicalPosition} for events in metric regions, or a \texttt{WallClockTime} for events in proportional regions, etc.). Event-local positions are part of the event's intrinsic data, not references to other objects. Operations that shift a voice or region update the contained events' positions directly; they do not need anchor indirection because the containment relationship makes the dependency explicit. Implementations \MAY{} compute and cache absolute positions from anchors for performance; such caches \MUST{} be invalidated when their underlying anchored objects change. \end{requirement} \begin{requirement} The variant of an \texttt{AnchorOffset} \MUST{} agree with the time model of the anchor target's enclosing region: \begin{itemize} \item For targets in metric regions: \texttt{AnchorOffset::Musical} or \texttt{AnchorOffset::Zero}. \item For targets in proportional regions: \texttt{AnchorOffset::WallClock} or \texttt{AnchorOffset::Zero}. \item For targets in aleatoric regions: any offset variant consistent with the region's \texttt{AleatoricAnchoringDiscipline} (Section~\ref{sec:graph:aleatoric-discipline}). \end{itemize} Implementations \MUST{} reject anchors whose offset variant contradicts the target region's time model. \end{requirement} \subsection{Anchor Resolution} Resolving a time anchor to an absolute position is a pure function of the score graph: locate the anchored object, query its position within its region, and add the offset. Resolution \MUST{} be deterministic. \subsection{Anchor Stability Under Edits} The chosen anchor type determines what edits the anchor survives: \begin{itemize} \item An \texttt{Event} anchor survives any edit that does not delete the anchored event. Edits that move the event (insertions before it, tempo changes) move the anchor with it. \item A \texttt{Measure} anchor survives insertions and deletions of other measures, and survives reordering. It is invalidated only by deletion of the anchored measure. \item A \texttt{Region} anchor survives edits within the region. It is invalidated only by deletion of the region. \item A \texttt{WallClock} anchor is unaffected by musical edits but may correspond to different musical positions after tempo changes. \end{itemize} \begin{requirement} When an anchored object is deleted, anchors targeting it \MUST{} be either re-anchored to a surviving object or marked as orphaned. The semantic operations chapter (Chapter~\ref{ch:semops}) specifies re-anchoring rules per operation. \end{requirement} \section{Time Signatures and Meter} \label{sec:time:meter} A time signature defines the metric organization of a passage: where bar lines fall, where beats group, and what accent pattern is implied. Time signatures are objects in the score graph; they are not merely display numerator/denominator pairs. \begin{lstlisting}[language=Rust] pub struct TimeSignature { /// Display form. Determines what the performer sees. pub display: TimeSignatureDisplay, /// The total duration of one measure under this signature. pub measure_duration: MusicalDuration, /// Beat groupings within the measure. Drives beaming, accent /// inference, and the notational-decomposition pre-pass. pub beat_groups: Vec, } pub enum TimeSignatureDisplay { /// Standard: a numerator over a power-of-two denominator. Standard { numerator: u16, denominator: PowerOfTwo }, /// Compound display: e.g., 3+2+3/8. Compound { numerators: Vec, denominator: PowerOfTwo }, /// Irrational meter: denominator is not a power of two. /// e.g., 4/6, 5/12. The denominator names a non-power-of-two /// note value. Irrational { numerator: u16, denominator: NonZeroU16 }, /// Mixed denominators: e.g., 1/4 + 1/8 + 1/16 displayed additively. MixedDenominators { components: Vec<(u16, NonZeroU16)> }, /// No visible time signature (free meter or proportional). None, /// Custom symbol (cut time, common time, etc.) or /// grammar-specific. Symbolic(SymbolId), } pub struct BeatGroup { /// Duration of this beat group. pub duration: MusicalDuration, /// Subdivision hint for beaming and accent. pub subdivision: Option, /// Accent strength relative to other beat groups in the measure. /// Higher values indicate stronger accents. pub accent: u8, } \end{lstlisting} \begin{requirement} The sum of the durations of a time signature's beat groups \MUST{} equal the measure duration. Implementations \MUST{} reject time signatures whose beat groups do not sum to the measure duration. \end{requirement} \subsection{Irrational Meters} Time signatures whose denominator is not a power of two (e.g., 4/6, 5/12) are first-class. Their measure duration is computed as a rational: a 4/6 measure has duration $\frac{4}{6} = \frac{2}{3}$ whole notes. No special case in the time model; only the display layer treats the unusual denominator. \subsection{Polymetric and Polytemporal Scores} A score \MAY{} have different time signatures concurrent on different staves. Each staff carries its own meter sequence; bar lines align where the rational positions coincide, and may misalign otherwise. The score graph chapter (Chapter~\ref{ch:graph}) defines how concurrent meters are represented; this chapter establishes only that the time model supports them. \section{Tempo and the Tempo Map} \label{sec:time:tempomap} The tempo map is the function mapping musical positions to wall-clock positions. It is a piecewise definition over musical time: \begin{lstlisting}[language=Rust] pub struct TempoMap { /// Piecewise segments, in non-overlapping musical-time order. pub segments: Vec, /// Tempo before the first segment, if any. None means the first /// segment's start_tempo applies from time zero. pub initial: Option, } pub struct TempoSegment { /// Start of this segment, as a TimeAnchor. pub start: TimeAnchor, /// End of this segment. None means "valid until the next segment /// in segments order, or until the end of the score if this is /// the final segment." Implementations resolve None lazily. pub end: Option, /// Tempo at the start of this segment. pub start_tempo: Tempo, /// Tempo at the end of this segment. /// Required if shape != Constant; ignored (and SHOULD be None) /// if shape == Constant. A Constant segment holds start_tempo /// throughout. pub end_tempo: Option, /// Shape of the tempo change over this segment. pub shape: TempoShape, } pub enum TempoShape { /// Constant tempo throughout: end_tempo MUST be None or equal to /// start_tempo. Constant, /// Linear interpolation from start_tempo to end_tempo. Linear, /// Exponential (continuous-rate) interpolation from start_tempo /// to end_tempo. Exponential, /// Arbitrary curve, parameterized by control points. The control /// points define intermediate tempos between start_tempo and /// end_tempo. Curve(TempoCurve), } pub struct Tempo { /// Beats per minute. The beat unit is part of the tempo /// specification: a quarter-note BPM and a dotted-quarter BPM /// at the same numeric value differ in actual rate. pub bpm: f64, /// Note value taken as one beat. pub beat_unit: MusicalDuration, } \end{lstlisting} \begin{requirement} The tempo map's segments \MUST{} be non-overlapping when resolved to absolute musical positions, and \MUST{} appear in monotonically-increasing start order. Two adjacent segments where the earlier segment's resolved end equals the later segment's resolved start form a continuous tempo curve; the later segment's \texttt{start\_tempo} \SHOULD{} equal the earlier segment's resolved end tempo when continuity is intended. A segment with \texttt{end == None} extends until the next segment's resolved start or, if no later segment exists, until the end of the score. Implementations \MUST{} resolve open-ended segments deterministically. At musical positions not covered by any segment (gaps between segments, or before the first segment when \texttt{initial} is \texttt{None}), the tempo \MUST{} be the most recent applicable segment's terminating tempo, or the next applicable segment's \texttt{start\_tempo} if no earlier segment exists. \end{requirement} \subsection{Conversion} Conversion between musical and wall-clock time integrates the tempo map. For constant segments, conversion is multiplication. For linear and exponential segments, conversion has a closed-form solution. For curve segments, conversion is numerical. \begin{requirement} \label{req:time:linear-interpolates-speed} For a \texttt{TempoShape::Linear} segment, the quantity that varies linearly across the segment is \emph{speed} --- whole notes per second --- as a function of fractional position within the segment, \emph{not} beats-per-minute and \emph{not} beat period. Speed is derived from a \texttt{Tempo} as $\text{speed} = \text{bpm} \times w(\text{beat\_unit}) / 60$, where $w(\cdot)$ is the beat unit's value in whole notes. Writing $s_0, s_1$ for the segment's start and end speeds and $u \in [0,1]$ for fractional position, $s(u) = s_0 + (s_1 - s_0)\,u$, and the wall-clock duration of the segment of musical length $\ell$ is the closed form $\int_0^1 \ell\,\mathrm{d}u / s(u) = (\ell / (s_1 - s_0))\, \ln(s_1 / s_0)$ (and $\ell / s_0$ when $s_1 = s_0$). A \texttt{TempoShape::Exponential} segment interpolates speed \emph{geometrically}: $s(u) = s_0\,(s_1/s_0)^{u}$, i.e. a constant continuous rate of change of speed. \end{requirement} \begin{rationale} ``Linear interpolation from \texttt{start\_tempo} to \texttt{end\_tempo}'' is ambiguous about which quantity is linear: bpm, period, or speed. We pin \emph{speed} because it is the only beat-unit-agnostic choice. A tempo map may change beat unit across segments (a \texttt{quarter = 120} segment abutting a \texttt{dotted-quarter = 80} segment); interpolating bpm would make the wall-clock schedule depend on the arbitrary beat unit chosen to notate each endpoint, whereas interpolating speed depends only on the sounding rate. When both endpoints share a beat unit, speed-linear and bpm-linear coincide, so the common case matches a conductor's ``linear accelerando'' intuition. Because this choice fixes the derived wall-clock schedule for every playback engine, it is stated normatively rather than left to the implementation. \end{rationale} \begin{requirement} The tempo map \MUST{} expose two conversion functions: \texttt{musical\_to\_wallclock} and \texttt{wallclock\_to\_musical}. Both \MUST{} be deterministic: given identical tempo maps and inputs, they \MUST{} produce identical outputs across runs and platforms. Numerical integration and inversion, where used, \MUST{} use a deterministic algorithm with documented tolerance and iteration bounds. Tempo curves \MUST{} be monotonically increasing in musical time (i.e., tempo may vary but musical time always advances) so that \texttt{wallclock\_to\_musical} is single-valued. Implementations \MUST{} reject tempo segments whose shape produces a non-monotonic mapping (such curves are musically meaningless). \end{requirement} \begin{openquestion} The specific numerical-integration algorithm for \texttt{TempoShape::Curve} (Romberg integration, adaptive Gauss-Kronrod, or a normative method) and the specific root-finding algorithm for \texttt{wallclock\_to\_musical} are unresolved. Both algorithms affect canonical state and must therefore receive one of the dispositions in Appendix~\ref{app:determinism} Section~\ref{sec:det:open}: normatively specified in this document or a named companion, profile-declared by versioned identifier, or explicitly marked non-canonical. Until that disposition is made, cross-implementation byte equality of derived canonical state (notably tempo-driven audio scheduling) \MUSTNOT{} be claimed. \end{openquestion} \begin{rationale} Determinism of tempo conversion is necessary for reproducible audio output, click-track stability, and SMPTE sync. Non-determinism here would produce playback drift that compounds over long pieces. \end{rationale} \section{Sounding Duration and Notational Decomposition} \label{sec:time:notrhythm} An event has a single \emph{sounding duration} (an exact rational). Its \emph{notational decomposition} is the sequence of notehead values, augmentation dots, and ties used to draw that duration on the staff. The decomposition is computed from the sounding duration by a deterministic pre-pass as a \emph{canonical derived annotation} --- recomputed on materialization, never stored graph state --- under the same output model as the spelling pre-pass (Section~\ref{sec:pitch:prepass}). The user may accept the inferred decomposition or override it by authoring a decomposition attachment, which takes precedence. \subsection{The Notational Decomposition} \begin{lstlisting}[language=Rust] pub struct NotationalDecomposition { /// Sequence of notated components. Their durations sum to the /// event's sounding duration. pub components: Vec, /// Provenance, analogous to SpellingSource. pub source: DecompositionSource, } pub struct NotatedComponent { /// Base note value: whole, half, quarter, eighth, etc. pub base_value: NoteValue, /// Number of augmentation dots (0..=4 typical). pub dots: u8, /// Tuplet membership, if any. pub tuplet: Option, /// Whether this component is tied to the next. pub tied_to_next: bool, } pub enum DecompositionSource { UserChosen, Inferred, Imported { format: ForeignFormatId }, Propagated { from: EventId }, } \end{lstlisting} The structure deliberately mirrors the spelling attachment model (Section~\ref{sec:pitch:spelling}): same sources, same source-rank discipline, same pre-pass output model. Unlike spelling, decomposition precedence is \emph{not configurable} (ratified Pass~12): the fixed default source order \texttt{UserChosen} $>$ \texttt{Imported} $>$ \texttt{Propagated} $>$ \texttt{Inferred} applies, with the score's canonical attachment order as tie-break among equal-rank authored attachments; \texttt{DecompositionAttachment} carries no \texttt{priority} field and the graph carries no \texttt{DecompositionPrecedence} configuration. Making precedence configurable would add a canonical \texttt{Score} field (a schema-major change under the Binary Format companion's evolution rule) for which no consumer exists; the decision may be revisited at a future schema major if a use case appears. \subsection{The Decomposition Pre-Pass} When a score is materialized, the decomposition pre-pass computes inferred decompositions, reported as derived annotations; an authored higher-precedence attachment overrides the inferred value in the resolved annotation. An authored attachment targeting an event the pre-pass produces \emph{no} inferred decomposition for (ungriddable, non-metric, or an inapplicable event kind) surfaces as the resolved annotation on its own, per Requirement~\ref{req:pitch:authored-uninferred} --- authoring is how a user notates exactly what the algorithm cannot infer. The algorithm is, in outline: \begin{enumerate} \item For each event in time order within a voice and measure, examine the sounding duration and the current beat-group context. \item Decompose the duration into the longest leading note value that fits within the current beat group without crossing a stronger accent boundary. \item If the duration is not exhausted, produce a tie and continue decomposition from the next beat-group boundary. \item Augmentation dots are applied where they shorten the decomposition without crossing an accent boundary that the dot would obscure. \item Tuplet membership is recorded for events that fall within a tuplet group (Section~\ref{sec:time:tuplets}). \end{enumerate} \begin{requirement} The decomposition pre-pass \MUST{} be deterministic: given identical materialized graph, configuration, and algorithm version, it \MUST{} produce identical decompositions. Its output is a derived annotation, never stored graph state. Implementations \MAY{} cache and \MAY{} recompute incrementally (re-running only the affected voice and measure, with propagation only as far as the context is altered); neither may be observable in the annotations produced. \end{requirement} \begin{requirement} \label{req:time:decomposition-algorithm} \textbf{Decomposition algorithm disposition (ratified Pass 12).} The decomposition pre-pass receives the \emph{profile-declared by versioned identifier} disposition of Appendix~\ref{app:determinism} Section~\ref{sec:det:open}. The reserved identifier \texttt{DecompositionAlgorithmId} \texttt{"default"} denotes, at version~1, the integer-grid metric splitter, whose scope bounds are part of its normative definition: \begin{itemize} \item a single governing meter per region --- a mid-region meter change reduces cleanly into the region's grid (the Operation Catalog pins those semantics), but derived notation honours only the first governing meter until a wider algorithm version lands; \item the region origin is assumed to fall on a barline (anacrusis/pickup handling deferred); \item compound-meter beat grouping uses the dyadic default; \item tuplet nesting and cross-beat tuplet members are out of scope; \item at most one augmentation dot (\texttt{MAX\_DOTS}~$=$~1); a double-dotted value is written as tied components --- correct, if not the most compact. \end{itemize} A wider algorithm is a \emph{version bump}, which deterministically invalidates derived output under the derived-annotation model --- no state migration. Style-choice selection among multiple valid decompositions (e.g., $\frac{3}{8}$ in $\frac{4}{4}$ as a dotted quarter versus a quarter tied to an eighth) is fixed by the versioned algorithm; user-configurable alternate rule sets, if ever wanted, are new registered identifiers. A profile requesting any identifier other than a registered one \MUST{} error; implementations \MUSTNOT{} silently substitute. \end{requirement} \section{Tuplets as Grouping Objects} \label{sec:time:tuplets} A tuplet is a grouping object in the score graph that records notational intent: that a span of events forms a triplet, quintuplet, or other irregular grouping with a visible bracket and number. Tuplets do not modify the sounding durations of their members; sounding durations are always concrete rationals. \begin{lstlisting}[language=Rust] pub struct Tuplet { pub id: TupletId, /// The actual:notated ratio. A triplet is 3:2; a quintuplet is /// 5:4; an irregular grouping like 7:4 has ratio 7 over 4. pub ratio: TupletRatio, /// Range of events in this tuplet, by event ID. pub members: Vec, /// Display: bracket style, number placement, parentheses, etc. pub display: TupletDisplay, /// Nesting: if this tuplet is itself inside another, the parent. pub parent: Option, } pub struct TupletRatio { // Fields are private: a TupletRatio is constructed only through the // checked `new`, so a degenerate ratio is never representable (see // the requirement below). The fields are read via `actual()`/`notated()`. actual: u32, notated: u32, } impl TupletRatio { /// Returns `None` for a degenerate ratio (either term zero, or /// `actual == notated`); otherwise the well-formed ratio. pub fn new(actual: u32, notated: u32) -> Option { /* checked */ } pub fn actual(&self) -> u32 { /* ... */ } pub fn notated(&self) -> u32 { /* ... */ } } \end{lstlisting} \begin{requirement} \label{req:time:tuplet-ratio-construction} A \texttt{TupletRatio} is \emph{degenerate} if either term is zero or if \texttt{actual == notated} (a ratio that expresses no augmentation or diminution). Degenerate ratios \MUST{} be rejected at construction: a conforming constructor of \texttt{TupletRatio} (and therefore of any \texttt{Tuplet}) \MUST{} fail rather than produce a value with \texttt{actual == 0}, \texttt{notated == 0}, or \texttt{actual == notated}. This is a construction-time rejection, not a runtime graph invariant (Section~\ref{sec:graph:invariants}): a degenerate ratio is never a representable graph state, so no invariant restores it after the fact. \end{requirement} \subsection{Tuplet Nesting} Tuplets \MAY{} nest to arbitrary depth. The sounding duration of an event inside nested tuplets is computed directly from its rational duration, not from the tuplet ratios; the ratios are notational and analytical. \begin{rationale} Storing sounding durations directly (rather than as base-value times a stack of tuplet ratios) keeps the time model regular: every event has a single rational duration, full stop. Moving a tuplet, splitting it, or deleting it never alters the durations of its members. Tuplets thus become a pure annotation layer, which dramatically simplifies edit operations. \end{rationale} \subsection{Tuplet Consistency} \begin{requirement} A tuplet's notated ratio \MUST{} be consistent with the sounding durations of its members: the sum of member durations \MUST{} equal the duration that the tuplet's notated value indicates when scaled by the ratio. For example, a 3:2 eighth-note triplet of three members has total sounding duration $\frac{1}{4}$; the sum of the three member durations \MUST{} equal $\frac{1}{4}$. This is a structural invariant. Edit operations that would violate it \MUST{} either adjust member durations or be rejected. The semantic operations chapter specifies which. \end{requirement} \section{Region Time Models} \label{sec:time:regions} A region of the score declares one of three time models. The declared model determines how positions within the region are interpreted, displayed, and played back. \begin{lstlisting}[language=Rust] pub enum RegionTimeModel { /// Metric time: measures, beats, exact rational positions. /// The dominant model for Western notated music. Metric(MetricTimeModel), /// Proportional time: events placed in space, where horizontal /// position is wall-clock time. No measures, no beats. Proportional(ProportionalTimeModel), /// Aleatoric time: events with partial or unspecified ordering. Aleatoric(AleatoricTimeModel), } \end{lstlisting} \subsection{Metric Time} \begin{lstlisting}[language=Rust] pub struct MetricTimeModel { /// Sequence of meter changes within this region. pub meters: Vec, /// Tempo map for this region. May inherit from the score. pub tempo: TempoMapReference, } \end{lstlisting} Metric regions have measures, beats, and time signatures as described in Sections~\ref{sec:time:meter} and~\ref{sec:time:tempomap}. Positions within them are \texttt{MusicalPosition} values. \subsection{Proportional Time} \begin{lstlisting}[language=Rust] pub struct ProportionalTimeModel { /// Total wall-clock duration of the region. pub duration: WallClockDuration, /// Horizontal space per second. Drives the engraver's spacing. /// Stored as a layout hint; the actual spacing is decided by the /// constraint solver. pub space_per_second: SpaceUnit, /// Optional reference grid markings (vertical lines every N /// seconds, for example) for performer reference. pub grid: Option, } \end{lstlisting} Events in proportional regions carry \texttt{WallClockTime} positions directly. There are no measures, no beats, no rational durations. Notational duration is computed from wall-clock duration by the notational decomposition pre-pass using a proportional-mode rule set, which generally produces unbeamed events with explicit duration notations or graphic-duration symbols. \begin{requirement} Events in a proportional region \MUST{} use \texttt{WallClockTime} for positions and \texttt{WallClockDuration} for durations. The tempo map \MUSTNOT{} be applied to convert proportional-region positions; they are already in wall-clock time. \end{requirement} \subsection{Aleatoric Time} \begin{lstlisting}[language=Rust] pub struct AleatoricTimeModel { /// Ordering constraints among events: a directed acyclic graph /// where an edge a -> b means "a precedes b." pub ordering: EventOrderingDAG, /// Anchoring discipline: declares what event coordinate kinds /// this region permits and whether duration-bound kinds may /// be mixed. See Section~\ref{sec:time:aleatoric-anchoring}. pub anchoring: AleatoricAnchoringDiscipline, /// Optional per-event interval bounds. An event with start /// bounds [t1, t2] may begin at any time in that window. /// Bound kinds are constrained by the anchoring discipline. pub bounds: HashMap, /// Approximate or maximum total duration, for layout. pub duration_hint: WallClockDuration, } pub enum AleatoricAnchoringDiscipline { /// Events in this region carry musical-time coordinates only. Musical, /// Events in this region carry wall-clock coordinates only. WallClock, /// Events may carry either kind, but each event's position and /// duration kinds must agree with each other. Mixed-kind /// duration bounds are not permitted. EitherPerEvent, /// Events may carry either kind freely, and duration bounds /// may mix kinds (one bound musical, the other wall-clock). /// Used for free-time scores synchronized to external media. FreelyMixed, } pub struct EventBounds { pub start: Option, pub end: Option, } pub enum TimeBounds { MusicalRange { min: MusicalPosition, max: MusicalPosition }, WallClockRange { min: WallClockTime, max: WallClockTime }, Unbounded, } \end{lstlisting} Aleatoric regions support both unordered event collections (any permutation of events is valid) and time-bracket notation (events with specified start and end windows, à la Cage's later notation). The DAG encoding admits both as cases. \subsubsection{Anchoring Discipline} \label{sec:time:aleatoric-anchoring} \label{sec:graph:aleatoric-discipline} \begin{requirement} Every aleatoric region \MUST{} declare an \texttt{AleatoricAnchoringDiscipline}. Events within the region \MUST{} carry coordinate kinds consistent with the discipline: \begin{itemize} \item \texttt{Musical}: events use \texttt{EventPosition::Musical} and \texttt{EventDuration::Musical} (or \texttt{EventDuration::Indeterminate} whose bounds are musical). \item \texttt{WallClock}: events use \texttt{EventPosition::WallClock} and \texttt{EventDuration::WallClock} (or \texttt{EventDuration::Indeterminate} whose bounds are wall-clock). \item \texttt{EitherPerEvent}: events choose per-event, but an event's position and duration kinds \MUST{} agree, and \texttt{DurationBounds} \MUST{} use a single \texttt{ConcreteDuration} variant. \item \texttt{FreelyMixed}: no agreement required; \texttt{DurationBounds} \MAY{} mix kinds. \end{itemize} Implementations \MUST{} reject events whose coordinate kinds violate the region's declared discipline. \end{requirement} \begin{requirement} \label{req:time:ordering-dag-acyclic} The ordering DAG \MUST{} be acyclic. Cycles \MUST{} be rejected at construction. Two events with no path between them in the DAG are unordered. \end{requirement} \begin{rationale} A DAG with optional interval bounds expresses every aleatoric form the project intends to support: unordered collections (empty DAG), partial orderings (sparse DAG), strict orderings (chain DAG), and time-brackets (DAG plus bounds). Higher-level constructs (mobile-form notation, statistical scores) can be built atop this primitive in later specifications without altering the core. \end{rationale} \subsection{Concurrent Time Models} Different staves in the same score \MAY{} declare different time models. A metric solo line over a proportional aleatoric texture, for example, is expressible: each staff's region declares its own model. Synchronization between concurrent time models is by wall-clock time or by explicit cue points (see Chapter~\ref{ch:graph}). \section{The Notion of ``Now''} \label{sec:time:now} The core does not define playback state; it does not have a notion of ``current position'' or ``transport time.'' Such state belongs to the audio engine, which consumes the core's time model. The core defines only the static time structure of the score. \section{Forward References} \begin{itemize} \item Event identifiers (\texttt{EventId}), measure identifiers (\texttt{MeasureId}), and region identifiers (\texttt{RegionId}) are defined in Chapter~\ref{ch:graph}. \item The full event taxonomy (pitched, unpitched, rest, indeterminate, trajectory) is defined in Chapter~\ref{ch:graph}. \item Re-anchoring rules for time anchors when their targets are deleted are defined per-operation in Chapter~\ref{ch:semops}. \item \texttt{SpaceUnit} is defined normatively in Chapter~\ref{ch:graph} as of schema major~2 (it entered canonical state through \texttt{StaffLineConfiguration}). \texttt{TupletDisplay} and other purely display-oriented types are defined in Chapter~\ref{ch:layout-ir}. \end{itemize} % =========================================================================== \chapter{Tuning Systems and Pitch Spaces} \label{ch:tuning} This chapter specifies the three intertwined registries that complete the primitives layer: \emph{pitch spaces} (the analytical universes in which scale-position arithmetic lives), \emph{accidental registries} (the catalogs of position-modifying glyphs), and \emph{tuning systems} (the frequency-resolution mechanisms). It also specifies the hierarchical resolution rules that determine which tuning applies to each pitch in a score. \section{Design Principles} \label{sec:tuning:principles} \begin{description} \item[Pitch space and tuning system are distinct.] A pitch space defines what pitches exist and how they relate analytically. A tuning system defines what frequencies those pitches produce. The two are independent: a single pitch space (e.g., CMN) admits many tuning systems (12-TET, meantone, well-temperaments, just intonation, Pythagorean); a single tuning structure may be anchored at different reference frequencies. \item[Accidentals belong to pitch spaces.] An accidental defines a \emph{position modification} within a pitch space. The tuning system handles the frequency consequences of that modification. The chromatic semitone implied by a sharp has different cent-values in 12-TET, quarter-comma meantone, and Pythagorean tuning, but it is the same accidental denoting the same position. \item[Reference pitch is score-level, not tuning-level.] A score declares a reference pitch (position plus frequency); the tuning system defines structure relative to that reference. ``12-TET at A=440'' and ``12-TET at A=415'' share the same tuning system; the reference differs. \item[Resolution is hierarchical.] Tuning, pitch space, and accidental registry are resolved by walking outward from the pitch: pitch, voice, staff, region, score. Inheritance markers permit any level to defer to the enclosing scope. \item[Built-in defaults, extensible by users.] The core ships a baseline catalog of pitch spaces, tuning systems, and accidental registries sufficient for the common 95\% case. Scores and grammar plugins extend these without forking. \item[SMuFL primary, custom fallback.] Glyph references default to SMuFL codepoints. Custom glyphs are supported as fallback. SMuFL version is recorded per score. \end{description} \section{Pitch Spaces} \label{sec:tuning:space} A pitch space is the analytical universe in which scale-degree and interval relationships are defined. It is the algebra; the tuning system is the physics. \begin{lstlisting}[language=Rust] pub struct PitchSpace { pub id: PitchSpaceId, /// Human-readable name (e.g., "CMN 12-tone chromatic"). pub name: String, /// Optional description and provenance notes. pub description: Option, /// What positions exist and how they are structured. pub positions: PositionStructure, /// How intervals between positions are defined. pub interval_algebra: IntervalAlgebra, /// The accidental registry valid in this pitch space. pub accidental_registry: AccidentalRegistryId, /// The nominal registry: named position-letters /// (e.g., A-G for CMN). pub nominal_registry: NominalRegistryId, /// Rules governing how pitches transpose within this space. pub transposition: TranspositionBehavior, /// Default rules for the spelling pre-pass when this pitch space /// is active. pub spelling_rules: SpellingRuleSet, } \end{lstlisting} \subsection{Position Structure} \label{sec:tuning:positions} The \texttt{PositionStructure} declares the shape of the pitch space. Three families cover the vast majority of useful cases; a fourth is the escape hatch for grammars defined entirely by plugin. \begin{lstlisting}[language=Rust] pub enum PositionStructure { /// Chromatic: a fixed number of equally-numbered positions per /// octave, with no hierarchical diatonic substructure. /// Includes EDOs and serial 12-tone. Chromatic { positions_per_octave: u16 }, /// Diatonic over chromatic: a smaller set of named nominals /// (e.g., A-G for CMN) plus accidental modifications reaching /// the full chromatic range. CMN's structure. DiatonicOverChromatic { nominals_per_octave: u16, chromatic_positions_per_octave: u16, /// Mapping from each nominal to its position in the /// chromatic layer (e.g., C=0, D=2, E=4, F=5, G=7, A=9, B=11 /// for CMN). nominal_to_chromatic: Vec, }, /// Just intonation lattice: positions defined by exact rational /// ratios from a tonic, organized along prime-axis dimensions /// (3-limit, 5-limit, 7-limit, etc.). JiLattice { limit: u8, // prime limit generators: Vec, // one per prime dimension }, /// Grammar-defined: structure is opaque to the core and resolved /// by a grammar plugin. Used for maqam, gamelan, raga frameworks, /// and other systems whose position structure is not flat or /// hierarchical in the above senses. Registered(PositionStructureRegistryId), } \end{lstlisting} \begin{requirement} The \texttt{DiatonicOverChromatic} variant \MUST{} have a \texttt{nominal\_to\_chromatic} mapping of length equal to \texttt{nominals\_per\_octave}, with each entry strictly less than \texttt{chromatic\_positions\_per\_octave}. The mapping \MUST{} be strictly increasing. \end{requirement} \subsection{Interval Algebra} An interval algebra defines how distances between positions are computed and how transposition is realized. For chromatic spaces the algebra is integer arithmetic modulo the position count; for diatonic-over-chromatic the algebra distinguishes diatonic from chromatic intervals (a diatonic third may correspond to three or four chromatic semitones depending on quality). \begin{lstlisting}[language=Rust] pub enum IntervalAlgebra { /// Single-axis integer arithmetic. Two positions are subtracted /// to yield a single integer interval. Chromatic, /// Two-axis arithmetic distinguishing diatonic and chromatic /// components. CMN's algebra: a major third is (3 diatonic steps, /// 4 chromatic steps); a diminished fourth is (3 diatonic steps, /// 4 chromatic steps) -- distinguishable from the major third only /// by spelling intent. DiatonicChromatic, /// Multi-axis JI lattice arithmetic. JiVector { dimensions: u8 }, /// Grammar-defined algebra. Registered(IntervalAlgebraRegistryId), } \end{lstlisting} \subsection{Nominal Registry} A nominal registry catalogs the named position-letters of a pitch space. For CMN this is the seven-element set $\{C, D, E, F, G, A, B\}$. For 12-tone serial music the nominal registry may simply be the integers $0$ through $11$. For maqam the nominals are the names of maqam degrees. \begin{lstlisting}[language=Rust] pub struct NominalRegistry { pub id: NominalRegistryId, pub nominals: Vec, } pub struct NominalDefinition { pub id: NominalId, /// Canonical name. pub name: String, /// Display glyph, typically a letter or symbol. pub display: GlyphReference, /// Default position in the pitch space's chromatic layer, if /// applicable. pub default_position: Option, } \end{lstlisting} \subsection{Transposition Behavior} Transposition behavior governs how scale positions move under interval operations and how spellings follow. \begin{lstlisting}[language=Rust] pub enum TranspositionBehavior { /// Diatonic transposition: shift by N diatonic steps within a /// chosen key. Accidentals adjust to fit the destination key. Diatonic, /// Chromatic transposition: shift by N chromatic steps. Spellings /// follow rules of the SpellingRuleSet. Chromatic, /// Compound: support both, parameterized at the operation site. Compound, /// Grammar-defined. Registered(TranspositionRegistryId), } \end{lstlisting} \subsection{Spelling Rule Sets} A pitch space carries a default spelling rule set that the spelling pre-pass (Section~\ref{sec:pitch:prepass}) consults when inferring spellings for unspelled pitches. \begin{lstlisting}[language=Rust] pub struct SpellingRuleSet { pub id: SpellingRuleSetId, pub name: String, /// Algorithmic family this rule set belongs to. The specific /// algorithm is referenced by id and resolved against the /// implementation's registered spelling algorithms. pub algorithm: SpellingAlgorithmId, /// Algorithm-specific parameters. pub parameters: SpellingParameters, } \end{lstlisting} \begin{openquestion} The default spelling algorithm is now ratified (Requirement~\ref{req:pitch:spelling-algorithm}: \texttt{"default"} = Temperley-style line-of-fifths preference, version~1). Still open: the catalog of \emph{additional} registered spelling algorithms (candidates include Longuet-Higgins line-of-fifths distance, Cambouropoulos pitch-spelling, and Meredith PS13) and their parameter schemas, which are normative once registered. \end{openquestion} \section{Accidental Registries} \label{sec:tuning:accidentals} An accidental registry catalogs the position-modifying glyphs valid within a pitch space. Each accidental defines a glyph, a position modification, and engraving metadata. \begin{lstlisting}[language=Rust] pub struct AccidentalRegistry { pub id: AccidentalRegistryId, pub name: String, pub accidentals: Vec, /// Whether scores may extend this registry with score-local /// accidentals. Most registries permit this; some (e.g., a /// closed conformance profile) may not. pub extensible: bool, } pub struct AccidentalDefinition { pub id: AccidentalId, /// Canonical name. Used for serialization, accessibility, and /// foreign-format export. pub name: String, /// Glyph reference, typically SMuFL. pub glyph: GlyphReference, /// The position modification this accidental applies. pub modification: PitchSpaceModification, /// Engraving metadata. pub engraving: AccidentalEngraving, /// Combination behavior with other accidentals. pub combination: AccidentalCombination, } \end{lstlisting} \subsection{Pitch Space Modifications} \begin{lstlisting}[language=Rust] pub enum PitchSpaceModification { /// CMN-style integer chromatic alteration. -2 = double flat, /// -1 = flat, +1 = sharp, +2 = double sharp, etc. CmnChromatic(i8), /// Integer step offset in an EDO position space. EdoSteps(i16), /// Modification expressed as an exact rational ratio. Used for /// JI accidentals such as the HEJI syntonic-comma symbols /// (81/80) and septimal-comma symbols (64/63). JiRatio { numerator: i32, denominator: NonZeroU32 }, /// Modification expressed in cents. Used for Sagittal-style /// precise microtonal accidentals. Cents(f64), /// Modification defined by a grammar plugin. Registered(ModificationRegistryId), } \end{lstlisting} \begin{requirement} An accidental's modification \MUST{} be expressible in the interval algebra of every pitch space that references its registry. A \texttt{CmnChromatic} modification is valid only in spaces with \texttt{DiatonicChromatic} or compatible algebra; an \texttt{EdoSteps} modification is valid only in \texttt{Chromatic} or \texttt{Registered} spaces with appropriate compatibility. Implementations \MUST{} reject scores referencing an accidental in a space whose algebra does not admit the modification. \end{requirement} \subsection{Accidental Engraving Metadata} \begin{lstlisting}[language=Rust] pub struct AccidentalEngraving { /// Bounding box in staff-space units, relative to the glyph's /// anchor point. pub bounding_box: BoundingBox, /// Where the glyph attaches to the note: typically the geometric /// center or a custom anchor for compound glyphs. pub anchor: AnchorPoint, /// Advance width for horizontal spacing computations. pub advance_width: SpaceUnit, /// Stacking order when multiple accidentals attach to one note. /// Lower values are placed closer to the notehead. pub stacking_order: i32, /// Whether this glyph should be drawn with parentheses by default /// (e.g., editorial or cautionary accidentals). pub default_parenthesized: bool, } \end{lstlisting} \subsection{Combination Behavior} Most accidentals do not combine; a note carries one accidental at a time. Some systems (HEJI, certain microtonal notations) permit stacking multiple accidental glyphs on a single note to express compound modifications. \begin{lstlisting}[language=Rust] pub enum AccidentalCombination { /// Stands alone; replaces any prior accidental on the same note. Solitary, /// May stack with members of the listed compatibility groups. /// Stacking order is determined by the engraving metadata. Stacking { compatible_groups: Vec }, } \end{lstlisting} \subsection{Score-Local Extensions} A score \MAY{} extend a referenced accidental registry with additional accidental definitions, provided the registry's \texttt{extensible} flag is true. Extensions are stored on the score and override or augment the base registry during resolution. \begin{lstlisting}[language=Rust] pub struct ScoreAccidentalExtensions { pub base: AccidentalRegistryId, pub additions: Vec, pub overrides: Vec, } \end{lstlisting} \section{Glyph References and SMuFL} \label{sec:tuning:smufl} The Standard Music Font Layout (SMuFL) is the normative source for glyph references in the core. Every standard accidental, notehead, articulation, dynamic, and ornament has a SMuFL codepoint. Non-standard glyphs are supported via custom glyph references. \begin{lstlisting}[language=Rust] pub enum GlyphReference { /// SMuFL codepoint, resolved against the active SMuFL font. Smufl(u32), /// Custom glyph defined by the score or a plugin. Custom(CustomGlyphId), /// Composite glyph: multiple glyph references rendered as a /// single accidental. Used for compound HEJI symbols and similar. Composite(Vec), } \end{lstlisting} \subsection{SMuFL Versioning} \begin{requirement} Every score \MUST{} declare the SMuFL version it targets. Resolving a SMuFL glyph reference whose codepoint is not present in the active font's SMuFL version \MUST{} produce a deterministic fallback (the reference SMuFL substitution glyph, or a clearly-marked missing-glyph indicator); it \MUSTNOT{} silently fail. \end{requirement} \begin{lstlisting}[language=Rust] pub struct SmuflVersionRequirement { /// Minimum SMuFL version required by this score. pub minimum: SmuflVersion, /// SMuFL version this score was authored against. Used for /// detecting whether newer glyphs are in use. pub authored_against: SmuflVersion, } \end{lstlisting} \section{Tuning Systems} \label{sec:tuning:system} A tuning system maps positions in a pitch space to frequencies. Given a reference pitch (position plus frequency) and the system's resolution rules, every position in the space resolves to a determinate frequency. \begin{lstlisting}[language=Rust] pub struct TuningSystem { pub id: TuningSystemId, /// Human-readable name. pub name: String, /// The pitch space whose positions this tuning resolves. pub pitch_space: PitchSpaceId, /// How the tuning resolves positions to frequencies, given a /// reference pitch. The reference itself is supplied separately /// (see Section~\ref{sec:tuning:reference}). pub resolution: TuningResolution, /// Optional historical or provenance notes. pub description: Option, } \end{lstlisting} \subsection{Tuning Resolution} \begin{lstlisting}[language=Rust] pub enum TuningResolution { /// N-tone equal temperament: each step is the Nth root of the /// octave ratio. Includes 12-TET when N=12. EqualTemperament { divisions_per_octave: u16 }, /// Explicit per-position ratios. Each entry is a ratio relative /// to the reference position. PerPositionRatios(Vec), /// Procedural definition: a registered tuning function that /// computes frequencies from a reference. Historical temperaments /// (Werckmeister, Vallotti, meantone variants) live here. Function { function: TuningFunctionId, parameters: TuningParameters, }, /// A base resolution with per-position overrides. Used for /// split-accidental keyboards and similar irregular tunings. Overlay { base: Box, overrides: Vec, }, /// Imported from a tuning file (Scala .scl, MIDI Tuning Standard, /// etc.). The data is captured at import; the original file is /// referenced for provenance but is not required at runtime. Imported { format: TuningFileFormat, data: Arc, }, /// Adaptive tuning: resolution depends on the harmonic context /// at the moment of sounding. Used for adaptive JI and similar /// systems. Adaptive { function: AdaptiveTuningFunctionId, parameters: AdaptiveTuningParameters, }, } \end{lstlisting} \subsection{The Resolution Contract} \begin{requirement} Every tuning resolution \MUST{} expose a deterministic function from pitch-space position (plus, where applicable, harmonic context) to frequency in Hertz, given a reference pitch. Determinism \MUST{} hold across runs and platforms; numerical computations \MUSTNOT{} depend on floating-point rounding modes. \end{requirement} \subsection{Adaptive Tuning} Adaptive tuning systems compute frequency from position \emph{plus harmonic context}. In a 5-limit adaptive JI system, for example, the frequency of a given E depends on whether it is the major third of a C chord, the perfect fifth of an A chord, or a passing tone between other harmonic interpretations. \begin{lstlisting}[language=Rust] pub struct HarmonicContext { /// Concurrently sounding pitches at the moment of resolution. pub concurrent: Vec, /// Recently sounding pitches, with decay weighting. pub recent: Vec<(PitchId, f64)>, /// The active key or modal center, if known. pub key_context: Option, /// User-supplied context hints attached to the score. pub hints: Vec, } \end{lstlisting} The harmonic context is constructed by the audio engine (or by analysis tooling) and passed to the tuning resolution function. Static tuning systems ignore it; adaptive tuning systems consume it. \begin{requirement} Adaptive tuning resolution \MUST{} be a pure function of position and harmonic context. Implementations \MAY{} cache resolution results, but \MUST{} invalidate caches when the harmonic context changes. \end{requirement} \section{Reference Pitch} \label{sec:tuning:reference} The reference pitch is the anchor that converts the tuning system's structural ratios into absolute frequencies. It is a property of the score, not of the tuning system. \begin{lstlisting}[language=Rust] pub struct ReferencePitch { /// The pitch-space position chosen as the reference. /// Conventionally A4 in CMN, but configurable. pub position: PitchSpacePosition, /// The frequency in Hertz assigned to that position. /// Conventional values: 440 Hz (modern standard), 415 Hz (Baroque), /// 430 Hz (early Classical), 442 Hz (some modern orchestras). pub frequency_hz: f64, } \end{lstlisting} \begin{requirement} Every score \MUST{} declare a reference pitch. The reference pitch \MUST{} be expressible as a valid position within the score's default pitch space. The frequency \MUST{} be positive and finite. \end{requirement} \subsection{Multiple Reference Pitches} A score \MAY{} declare additional reference pitches at the staff or region level, overriding the score default. This supports mixed-instrumentation scores in which different instruments use different concert-pitch references (e.g., a piece for period and modern instruments with different A frequencies). \section{Score Tuning Context and Hierarchical Resolution} \label{sec:tuning:context} The \emph{score tuning context} is the top-level structure declaring the tuning environment for a score. Per-scope overrides extend it. \begin{lstlisting}[language=Rust] pub struct ScoreTuningContext { /// Default pitch space for the score. pub default_pitch_space: PitchSpaceId, /// Default tuning system. Its pitch_space must match (or be /// compatible with) the default_pitch_space. pub default_tuning_system: TuningSystemId, /// The reference pitch for the score. pub reference: ReferencePitch, /// Score-local accidental registry extensions. pub accidental_extensions: Vec, /// SMuFL version targeting. pub smufl: SmuflVersionRequirement, /// Per-scope overrides, in the order they apply. pub overrides: Vec, } pub struct TuningOverride { pub scope: TuningScope, pub pitch_space: Option, pub tuning_system: Option, pub reference: Option, } pub enum TuningScope { Voice(VoiceId), Staff(StaffId), Region(RegionId), Range { start: TimeAnchor, end: TimeAnchor, voices: VoiceSelector }, } \end{lstlisting} \subsection{Resolution Order} For a given pitch, resolution proceeds by walking outward through scopes from most specific to most general until each component (pitch space, tuning system, reference) is determined. \begin{requirement} Resolution \MUST{} proceed in the following order, halting at the first scope that supplies a non-inherited value for each component: \begin{enumerate} \item The pitch's own \texttt{AcousticPitch} (an explicit \texttt{TuningReference::Explicit} or \texttt{AcousticRealization::AbsoluteHz} short-circuits the walk). \item The voice containing the pitch. \item The staff containing the voice. \item Each region enclosing the pitch, from innermost to outermost. \item The score's default tuning context. \end{enumerate} Each of the three components (pitch space, tuning system, reference) is resolved independently along this chain. \end{requirement} \begin{rationale} Independent per-component resolution allows a passage to override only the parts that differ. A period-instrument inset in a modern orchestra score may override only the reference frequency (415 Hz instead of 440 Hz) while inheriting the pitch space and tuning structure from the parent context. Conversely, a microtonal passage in an otherwise 12-TET score may override the pitch space and tuning system but inherit the reference. \end{rationale} \subsection{Compatibility Constraints} \begin{requirement} When a tuning override is applied, the resolved tuning system's declared \texttt{pitch\_space} \MUST{} either equal the resolved pitch space or be declared compatible with it via a registered compatibility mapping. Implementations \MUST{} reject configurations in which the resolved pitch space and the resolved tuning system's pitch space disagree without a compatibility mapping. \end{requirement} \section{Built-in Catalog} \label{sec:tuning:builtin} The core ships with a baseline catalog of pitch spaces, tuning systems, and accidental registries sufficient for the common case. The catalog is normative: a conforming implementation \MUST{} provide these identifiers with the specified semantics. \subsection{Built-in Pitch Spaces} \begin{longtable}{p{3.5cm} p{10cm}} \toprule \textbf{Identifier} & \textbf{Description} \\ \midrule \endhead \texttt{cmn-12} & Common Music Notation, 7 diatonic nominals (A--G) over 12 chromatic positions. Standard sharps, flats, double-sharps, and double-flats. The default for Western tonal and post-tonal music. \\ \texttt{cmn-24} & CMN extended with 24-EDO quarter-tone accidentals. Used for Arabic-derived microtonal practice in CMN-compatible notation. \\ \texttt{edo-19} & 19-tone equal division of the octave. Notable for its closer-to-just major thirds. \\ \texttt{edo-22} & 22-tone equal division. Distinguishes harmonic intervals collapsed in 12-TET. \\ \texttt{edo-31} & 31-tone equal division. Approximates quarter-comma meantone almost exactly. \\ \texttt{edo-53} & 53-tone equal division. Excellent approximation of 5-limit JI. \\ \texttt{edo-72} & 72-tone equal division. Common in research and contemporary microtonal practice. \\ \texttt{ji-5limit} & 5-limit just intonation lattice with HEJI accidentals. Two-dimensional (prime axes 3, 5). \\ \texttt{ji-7limit} & 7-limit JI lattice with HEJI accidentals. Three-dimensional. \\ \texttt{ji-11limit} & 11-limit JI lattice with HEJI accidentals. Four-dimensional. \\ \texttt{maqam-base} & Skeletal maqam framework with quarter-flat and quarter-sharp accidentals. Deep coverage of specific maqamat is the province of grammar plugins. \\ \texttt{gamelan-slendro} & Five-tone slendro framework. \\ \texttt{gamelan-pelog} & Seven-tone pelog framework. \\ \bottomrule \end{longtable} \subsection{Built-in Tuning Systems} \begin{longtable}{p{4cm} p{9.5cm}} \toprule \textbf{Identifier} & \textbf{Description} \\ \midrule \endhead \texttt{tet-12} & 12-tone equal temperament. The default. \\ \texttt{tet-19} & 19-tone equal temperament. \\ \texttt{tet-22} & 22-tone equal temperament. \\ \texttt{tet-31} & 31-tone equal temperament. \\ \texttt{tet-53} & 53-tone equal temperament. \\ \texttt{tet-72} & 72-tone equal temperament. \\ \texttt{pythagorean} & Pure-fifth (3:2) Pythagorean tuning. \\ \texttt{meantone-1/4-comma} & Quarter-comma meantone (pure major thirds). \\ \texttt{meantone-1/6-comma} & Sixth-comma meantone. \\ \texttt{meantone-1/5-comma} & Fifth-comma meantone. \\ \texttt{werckmeister-iii} & Werckmeister III well temperament. \\ \texttt{werckmeister-iv} & Werckmeister IV well temperament. \\ \texttt{vallotti} & Vallotti well temperament. \\ \texttt{kirnberger-ii} & Kirnberger II well temperament. \\ \texttt{kirnberger-iii} & Kirnberger III well temperament. \\ \texttt{young-ii} & Thomas Young's second temperament. \\ \texttt{ji-static-5limit-C} & Static 5-limit JI anchored to C tonic. \\ \texttt{ji-static-5limit-G} & Static 5-limit JI anchored to G. \\ \texttt{ji-static-5limit-D} & Static 5-limit JI anchored to D. \\ \texttt{ji-adaptive-5limit} & Adaptive 5-limit JI; resolves based on harmonic context. \\ \bottomrule \end{longtable} \begin{requirement} All built-in catalog identifiers \MUST{} resolve in a conforming implementation. The semantics specified above are normative. Implementations \MAY{} provide additional built-ins; they \MUSTNOT{} redefine the semantics of those listed. \end{requirement} \subsection{Default Score Configuration} A newly-created score with no explicit tuning context \MUST{} default to: \begin{itemize} \item Pitch space: \texttt{cmn-12}. \item Tuning system: \texttt{tet-12}. \item Reference pitch: A4 = 440\,Hz. \item SMuFL version: the highest SMuFL version supported by the implementation. \end{itemize} \section{Forward References} \begin{itemize} \item \texttt{VoiceId}, \texttt{StaffId}, \texttt{RegionId}, \texttt{VoiceSelector}, and the score graph's hierarchical organization are defined in Chapter~\ref{ch:graph}. \item \texttt{KeyContext} and the harmonic-context construction machinery are partially defined in Chapter~\ref{ch:graph} and completed in the audio engine specification (out of scope for this document). \item The specific spelling algorithms referenced by \texttt{SpellingAlgorithmId} remain an open question; see the related open question in Section~\ref{sec:pitch:prepass}. \end{itemize} % =========================================================================== \chapter{The Score Graph} \label{ch:graph} This chapter specifies the score graph: the in-memory representation of all musical content in a score. The graph is the canonical truth about the music; layout, serialization, and editing operations are downstream projections and consumers of it. This chapter is the largest in the specification because it threads together every primitive defined in Chapters~\ref{ch:pitch}--\ref{ch:tuning} into a coherent whole. \section{Design Principles} \label{sec:graph:principles} \begin{description} \item[Hybrid topology.] The graph is a tree of containment overlaid with cross-cutting structures that hold references. The tree determines existence: deleting a parent deletes its descendants. Cross-cutting structures hold references that may become dangling and require re-anchoring per the rules defined in this chapter and in Chapter~\ref{ch:semops}. \item[Canvas before staff.] The spatial root of a score is the canvas, a 2D space partitioned into regions. Staves exist inside regions, not as a top-level concept. This permits regions of free graphic content to coexist with regions of staff-based CMN without forcing one paradigm to subsume the other. \item[Arena storage.] Events are stored in a flat arena owned by the score. Voices, cross-cutting structures, and analysis layers hold event identifiers, not events. Arena storage enables fast bulk iteration, simple cross-cutting reference, and clean CRDT semantics. \item[Parts are projections.] A part is a view onto the score defined by a part-extraction specification. Parts do not live in the canvas; they are separate objects that select and reshape canvas content for printed extraction. \item[Typed identifiers.] Every named object kind has its own identifier type. Cross-kind confusion is a compile-time error. \item[Identifiers are stable and deterministic.] Identifiers are assigned at creation, never reassigned, and generated by a scheme that is deterministic per replica. \end{description} \section{Top-Level Score Structure} \label{sec:graph:score} The score is the root object. All other objects in the specification exist within or reference into a score. Conflict records are deliberately \emph{not} part of the score graph: the \texttt{ConflictRegistry} produced by canonical reduction is a component of the canonical \emph{materialized state} (Chapter~\ref{ch:semops}), recomputed with it, and persisting there until resolved or dismissed by explicit operations. \begin{lstlisting}[language=Rust] pub struct Score { /// Bibliographic and authorship metadata. pub metadata: ScoreMetadata, /// The spatial root: regions and their staff/graphic content. pub canvas: Canvas, /// Abstract instrument definitions used by staves. pub instruments: Vec, /// Globally-identified staves of the score. Each region's /// staff instances reference these by StaffId. pub staves: Vec, /// Optional staff groupings (grand staves, choral groups, /// bracketed orchestral sections). Used by engraving and parts. pub staff_groups: Vec, /// Per-part view definitions for extraction. pub parts: Vec, /// Cross-cutting structures: slurs, ties, hairpins, markers, /// graphic gestures, comments, and analytical annotations. pub cross_cutting: CrossCuttingRegistry, /// Tuning systems, pitch spaces, and reference pitch. pub tuning_context: ScoreTuningContext, /// The score-level tempo map. Regions may declare local tempo /// maps that override this. pub tempo_map: TempoMap, /// Flat arena of all events in the score. pub events: EventArena, /// Spelling attachments index (see Chapter 2). pub spelling_attachments: SpellingAttachmentIndex, /// Notational decomposition attachments index (see Chapter 3). pub decomposition_attachments: DecompositionAttachmentIndex, /// Analysis layers and view definitions. pub analysis_layers: Vec, pub views: Vec, /// Replica identifier and identifier generation state. pub identity: IdentityContext, } pub struct StaffGroup { pub id: StaffGroupId, pub name: Option, pub kind: StaffGroupKind, pub members: Vec, } pub enum StaffGroupKind { /// Grand staff: typically piano, organ, harp. GrandStaff, /// Bracketed group: orchestral sections. Bracket, /// Sub-bracket: divisi within a section. SubBracket, /// Choral group: SATB and similar. Choral, /// Custom group defined by a layout extension. Registered(StaffGroupKindRegistryId), } \end{lstlisting} \subsection{Score Metadata} \begin{lstlisting}[language=Rust] pub struct ScoreMetadata { pub title: Option, pub subtitle: Option, pub composer: Option, pub lyricist: Option, pub arranger: Option, pub copyright: Option, pub creation_timestamp: Timestamp, pub modification_timestamp: Timestamp, pub additional: Vec, } pub struct MetadataEntry { pub key: String, pub value: MetadataValue, } /// The value of an additional metadata entry (defined schema major 2). /// Closed small union; growth is by appended variant. pub enum MetadataValue { Text(String), Integer(i64), Flag(bool), } /// A calendar timestamp (defined schema major 2): nanoseconds since the /// Unix epoch, UTC, no zone. Distinct from WallClockTime, which is /// *performance* time within a score. The zero value is the "unset" /// convention (genesis without a declared creation time). pub struct Timestamp(pub i64); \end{lstlisting} The metadata structure is deliberately small. Extended bibliographic, catalog, and editorial-apparatus metadata is the province of foreign- format mappings and analytical layers, not the core. \texttt{additional} is an \emph{ordered authored list}, not a map (defined schema major 2): entry order is preserved verbatim in canonical bytes, and duplicate keys are permitted --- foreign formats legitimately carry repeated keys (multiple contributors, multiple identifiers of one scheme). Consumers wanting map semantics take the first entry per key. \begin{requirement} \label{req:graph:metadata-timestamps} \textbf{Timestamps are strictly authored (ratified with schema major 2).} \texttt{creation\_timestamp} and \texttt{modification\_timestamp} are stored, \emph{authored} values: genesis takes the creation timestamp explicitly (the empty-document constructor defaults it to the epoch-zero unset convention), and a \texttt{SetMetadata} operation carries both verbatim as part of the metadata value. Implementations \MUSTNOT{} write either field implicitly --- an automatic modification-time update would rewrite canonical bytes on every edit, churning content addresses and breaking replica byte-agreement for identical operation histories. A client that wants a live modification time surfaces one from non-canonical state (the operation log's stamps); it does not store it here. \end{requirement} \section{Identifiers} \label{sec:graph:ids} Every named object in the graph carries a typed 128-bit identifier. \begin{lstlisting}[language=Rust] pub struct EventId(u128); pub struct VoiceId(u128); pub struct StaffId(u128); pub struct StaffInstanceId(u128); pub struct StaffGroupId(u128); pub struct RegionId(u128); pub struct InstrumentId(u128); pub struct PartDefinitionId(u128); pub struct MeasureId(u128); pub struct BarlineAlignmentGroupId(u128); pub struct SlurId(u128); pub struct TieId(u128); pub struct BeamId(u128); pub struct SpannerId(u128); pub struct MarkerId(u128); pub struct AnalyticalAnnotationId(u128); pub struct CommentId(u128); pub struct GraphicObjectId(u128); pub struct GraphicGestureId(u128); pub struct TimeSignatureId(u128); pub struct ConflictId(u128); pub struct TransactionId(u128); // ... and so on for every named object kind /// A tagged identifier over every typed identifier kind in the /// score graph. Used wherever an object is referenced generically /// (cross-cutting endpoints, conflict affected_objects, repair /// records, edit barriers). The variant tag is part of canonical /// content: distinct variants with the same underlying u128 are /// distinct TypedObjectIds. pub enum TypedObjectId { Event(EventId), Pitch(PitchId), Voice(VoiceId), Staff(StaffId), StaffInstance(StaffInstanceId), StaffGroup(StaffGroupId), Region(RegionId), Instrument(InstrumentId), PartDefinition(PartDefinitionId), Measure(MeasureId), BarlineAlignmentGroup(BarlineAlignmentGroupId), Slur(SlurId), Tie(TieId), Beam(BeamId), Spanner(SpannerId), Marker(MarkerId), AnalyticalAnnotation(AnalyticalAnnotationId), Comment(CommentId), GraphicObject(GraphicObjectId), GraphicGesture(GraphicGestureId), TimeSignature(TimeSignatureId), AnalysisLayer(AnalysisLayerId), Tuplet(TupletId), RepeatStructure(RepeatStructureId), LyricLine(LyricLineId), ChordSymbol(ChordSymbolId), View(ViewId), /// Extension-defined object kind, identified by registry id /// plus the extension's own 128-bit identifier. Registered(ObjectKindRegistryId, u128), } impl TypedObjectId { /// Canonical byte form for hashing, ordering, and equality. /// Variant tag is encoded as a 16-bit big-endian discriminant /// followed by the variant payload's canonical bytes. pub fn canonical_bytes(&self) -> Vec { /* ... */ } } \end{lstlisting} \begin{requirement} \label{req:graph:typed-object-id-discriminants} The \texttt{TypedObjectId} variant tag \MUST{} be encoded as a 16-bit big-endian discriminant followed by the variant payload's canonical bytes. The discriminant of each variant is its declaration-order index in the following closed table; this assignment is normative and stable, and \MUSTNOT{} be reordered: \begin{center} \begin{tabular}{r l @{\qquad} r l} \toprule \textbf{Disc.} & \textbf{Variant} & \textbf{Disc.} & \textbf{Variant} \\ \midrule 0 & \texttt{Event} & 14 & \texttt{Spanner} \\ 1 & \texttt{Pitch} & 15 & \texttt{Marker} \\ 2 & \texttt{Voice} & 16 & \texttt{AnalyticalAnnotation} \\ 3 & \texttt{Staff} & 17 & \texttt{Comment} \\ 4 & \texttt{StaffInstance} & 18 & \texttt{GraphicObject} \\ 5 & \texttt{StaffGroup} & 19 & \texttt{GraphicGesture} \\ 6 & \texttt{Region} & 20 & \texttt{TimeSignature} \\ 7 & \texttt{Instrument} & 21 & \texttt{AnalysisLayer} \\ 8 & \texttt{PartDefinition} & 22 & \texttt{Tuplet} \\ 9 & \texttt{Measure} & 23 & \texttt{RepeatStructure} \\ 10 & \texttt{BarlineAlignmentGroup} & 24 & \texttt{LyricLine} \\ 11 & \texttt{Slur} & 25 & \texttt{ChordSymbol} \\ 12 & \texttt{Tie} & 26 & \texttt{View} \\ 13 & \texttt{Beam} & 27 & \texttt{Registered} \\ \bottomrule \end{tabular} \end{center} The object-kind set is closed at these 28 discriminants (\texttt{0}--\texttt{27}). Every named object kind in the score graph has a dedicated discriminant; kinds introduced by registered extensions \MUST{} use discriminant \texttt{27} (\texttt{Registered}) and are distinguished by their \texttt{ObjectKindRegistryId}, never by extending this table. The \texttt{Registered(reg, raw)} payload is encoded as the 128-bit \texttt{reg.canonical\_bytes()} (16 big-endian bytes) followed by the 16 big-endian bytes of the \texttt{raw} \texttt{u128}, for a total canonical form of $2 + 16 + 16 = 34$ bytes. \texttt{ObjectKindRegistryId} is a 128-bit value. \end{requirement} \subsection{Identifier Generation} \begin{requirement} Identifiers \MUST{} be 128-bit values composed of a 64-bit replica identifier and a 64-bit monotonic counter local to that replica. The replica identifier \MUST{} be unique among all replicas of the score; collision resistance is achieved by random initialization of the replica identifier at creation time with at least 64 bits of entropy from a cryptographically appropriate source. The counter \MUST{} be monotonically increasing within a replica and \MUSTNOT{} be reused, even if the corresponding object is deleted. \end{requirement} \begin{lstlisting}[language=Rust] pub struct IdentityContext { /// This replica's identifier. Generated at score creation. pub replica_id: ReplicaId, /// Monotonic counter for new identifiers. Per-kind counters /// optional; a single counter suffices. pub next_counter: u64, } pub struct ReplicaId(u64); impl ReplicaId { /// Reserved replica identifier for deterministically-derived /// system identifiers (system-promoted voices, content-derived /// conflict IDs, future deterministic synthetic identifiers). /// User-authored replicas MUST NOT use this value. pub const SYSTEM_DERIVED: ReplicaId = ReplicaId(0xffff_ffff_ffff_ffff); } \end{lstlisting} \begin{rationale} Replica-plus-counter identifiers are deterministic given a replica and a creation order, which simplifies reasoning about CRDT merge semantics and reproducible builds of generated scores. UUIDv7 was considered and rejected: the timestamp embedding leaks ordering information that the CRDT layer would have to reconstruct anyway, and the random portion gives less collision resistance per bit than a properly initialized replica identifier. \end{rationale} \subsection{System-Derived Identifier Namespace} \label{sec:graph:system-derived} Some identifiers are not author-authored: they arise during deterministic reduction (system-promoted voices, content-derived conflict identifiers, attachment-deduplication identifiers). To prevent collisions with user-authored identifiers, the spec reserves a dedicated replica namespace. \begin{requirement} The replica identifier \texttt{ReplicaId::SYSTEM\_DERIVED} (\texttt{0xffff\_ffff\_ffff\_ffff}) is reserved for deterministically-derived system identifiers. User-authored replicas \MUSTNOT{} use this value as their \texttt{ReplicaId}. Implementations creating a new score \MUST{} reject this value when generating the local replica identifier and \MUST{} regenerate the random portion until a non-reserved value is obtained. Because a system-derived identifier is content-derived, the identified object's intrinsic content is \emph{immutable}: a reduction \MUST{} refuse an operation that would rewrite the intrinsic content of a \texttt{SYSTEM\_DERIVED}-namespace object in place (ratified Pass~12; the Operation Catalog pins the precondition and its failure reason). Rewriting the content would silently invalidate the id's content derivation; the sanctioned path is minting a replacement object. System-derived identifiers within the \texttt{ReplicaId::SYSTEM\_DERIVED} namespace \MUST{} have their 64-bit counter portion derived deterministically by BLAKE3 truncation: \begin{lstlisting}[language=Rust] fn derive_system_counter( domain_tag: &[u8; 8], canonical_inputs: &[u8], ) -> u64 { let mut preimage = Vec::new(); preimage.extend_from_slice(domain_tag); preimage.extend_from_slice(canonical_inputs); let hash = blake3(&preimage); u64::from_be_bytes(hash[0..8].try_into().unwrap()) } \end{lstlisting} The built-in (reserved) domain tags for system-derived identifiers are a closed set of three: \begin{itemize} \item \texttt{"MUSCSVCE"} for system-promoted voices (Section~\ref{sec:graph:promoted-voices}). \item \texttt{"MUSCSPCH"} for system-derived pitches (rare; only when promotion logic introduces a synthetic pitch; Section~\ref{sec:graph:system-derived-pitch}). \item \texttt{"MUSCSANM"} for integrity-anomaly identifiers (Section~\ref{sec:graph:system-collisions}). Anomalies are core, not an extension concern, so their tag is built in alongside the other two. \item Additional domain tags introduced by registered extensions \MUST{} begin with \texttt{"MUSCS"}, have length exactly 8 bytes, and \MUSTNOT{} collide with the three reserved tags above. \end{itemize} Two replicas reducing the same operation set \MUST{} derive identical system identifiers from identical canonical inputs. \end{requirement} \subsection{System-Derived Counter Collisions} \label{sec:graph:system-collisions} The 64-bit counter portion of a system-derived identifier is derived by BLAKE3 truncation. Truncation to 64 bits offers sufficient collision resistance against accidental collision (roughly $2^{32}$ system-derived identifiers of the same kind before a 50\% chance of any pair colliding), but is weaker than the rest of the identifier story and is potentially vulnerable to deliberate construction of colliding canonical inputs. A collision means the identity layer has failed: canonical state derived from a colliding identifier cannot be trusted. \begin{lstlisting}[language=Rust] /// An integrity anomaly that takes the document out of ordinary /// canonical operation. Distinct from ConflictRecord (an /// ordinary canonical-state fact): integrity anomalies indicate /// that the structural assumptions underlying canonical state /// have failed. pub struct IntegrityAnomaly { pub id: IntegrityAnomalyId, pub kind: IntegrityAnomalyKind, } pub enum IntegrityAnomalyKind { /// A system-derived identifier counter collision. SystemIdentifierCollision { kind: ObjectKind, colliding_counter: u64, input_set_a: SerializedCanonicalInputs, input_set_b: SerializedCanonicalInputs, }, /// An OperationSlot is Equivocated. The slot's candidates /// are referenced; reduction has excluded the operation. OperationSlotEquivocated { operation_id: OperationId, }, /// A replica's stream has been quarantined per the HLC /// monotonicity rule. The AnomalousReplicaSegment is /// referenced. ReplicaStreamQuarantined { replica: ReplicaId, first_bad_counter: u64, }, /// A registered anomaly defined by an extension or /// transport. Registered(IntegrityAnomalyRegistryId), } /// The kind of object whose system-derived identifier collided. /// Open vocabulary, but intentionally narrower than TypedObjectId: /// only the kinds actually minted into the system-derived namespace /// appear here. pub enum ObjectKind { Voice, // system-promoted voices (MUSCSVCE) Pitch, // content-derived synthetic pitches (MUSCSPCH) Registered(OperationKindRegistryId), } \end{lstlisting} \begin{requirement} \label{req:graph:integrity-anomaly-id} An \texttt{IntegrityAnomalyId} \MUST{} be derived by the system-derived identifier function of Section~\ref{sec:graph:system-derived} with the reserved domain tag \texttt{"MUSCSANM"} over the anomaly kind's canonical bytes: \texttt{derive\_system\_id::(b"MUSCSANM", \&kind.to\_canonical\_bytes())}. Because the identity is content-derived from the kind, two replicas observing the same structural failure derive the same anomaly identifier and therefore agree on anomaly identity across the network. \end{requirement} \begin{requirement} \label{req:graph:object-kind-vocab} \texttt{ObjectKind} (the kind field of \texttt{SystemIdentifierCollision}) is an \emph{open} vocabulary whose normative core set is exactly \texttt{Voice} and \texttt{Pitch}, with a \texttt{Registered} escape for extension-introduced system-derived kinds. It is deliberately narrower than \texttt{TypedObjectId}'s 28-kind table (Requirement~\ref{req:graph:typed-object-id-discriminants}): \texttt{ObjectKind} enumerates only the kinds that are actually minted into the \texttt{ReplicaId::SYSTEM\_DERIVED} namespace --- today, promoted voices and synthetic pitches --- because those are the only kinds for which a system-derived counter collision is possible. A new core variant is added here only when a new object kind begins being minted into the system-derived namespace. The discriminant byte of \texttt{ObjectKind} is its declaration-order index in the listing above: \texttt{Voice} $= 0$, \texttt{Pitch} $= 1$, \texttt{Registered} $= 2$. Because \texttt{ObjectKind} is the \texttt{kind} field of a \texttt{SystemIdentifierCollision} anomaly, this byte enters the canonical-input preimage of the resulting \texttt{IntegrityAnomalyId} (Requirement~\ref{req:graph:integrity-anomaly-id}); two replicas observing the same structural failure agree on its identity only if they agree on this byte. The assignment is therefore normative and stable and \MUSTNOT{} be reordered. \end{requirement} \begin{requirement} Implementations \MUST{} perform a collision check during reduction whenever a new system-derived identifier is minted. If a newly derived 64-bit counter, within the same typed identifier kind and within the \texttt{ReplicaId::SYSTEM\_DERIVED} namespace, collides with a previously-derived counter from \emph{different} canonical inputs: \begin{itemize} \item An \texttt{IntegrityAnomaly} of kind \texttt{SystemIdentifierCollision} is recorded in the document's diagnostic integrity-anomaly register. This register is distinct from the canonical conflict registry: conflict records are ordinary canonical-state facts; integrity anomalies indicate the structural assumptions underlying canonical state have failed. \item Canonical reduction \MUSTNOT{} continue past the collision. The colliding object's identifier is held pending recovery: neither input set is allowed to occupy the collided counter in canonical state. \item Readers \MUST{} open such a bundle in diagnostic recovery mode, surfacing the collision prominently and prohibiting ordinary editing of the affected portion of canonical state. \item Recovery is by external action: a profile-declared deterministic policy, a transport-level credential revocation, or an explicit user reconciliation operation. No automatic in-spec resolution is defined. \end{itemize} Authoring implementations \SHOULD{} detect predictable collisions before authoring (e.g., by canonicalizing inputs with additional disambiguating data such as the causally-greatest operation visible at authoring time) and \SHOULDNOT{} rely on collision frequency being negligible in adversarial scenarios. \end{requirement} \begin{rationale} Moving system-derived collisions out of \texttt{ConflictKind} reflects the difference in kind between the two. A \texttt{ConflictRecord} is an ordinary canonical-state fact: the user has two valid musical edits that conflict, and the resolution is a normal editing action. A system-derived identifier collision is not a musical conflict; it is a structural failure of the identity layer. Treating it as a conflict would let canonical state grow under the collision, which is precisely the wrong behavior for an integrity failure. Treating it as a diagnostic anomaly halts canonical growth until external recovery resolves the structural issue. \end{rationale} \subsection{System-Derived Pitch Identity} \label{sec:graph:system-derived-pitch} When promotion logic introduces a synthetic pitch (rare), its \texttt{PitchId} is content-addressed from the pitch's intrinsic identity rather than allocated from a replica counter, so that all replicas mint the same identifier for the same synthetic pitch. \begin{requirement} \label{req:graph:system-derived-pitch-id} A system-derived \texttt{PitchId} \MUST{} be derived by the function of Section~\ref{sec:graph:system-derived} with domain tag \texttt{"MUSCSPCH"} over a fixed canonical byte form of the pitch's \emph{intrinsic identity}, defined as the pair (\emph{scale position}, \emph{acoustic realization}). All variable-width string components of this preimage (nominal names, accidental and tuning catalog references) \MUST{} be length-prefixed and \MUST{} be NFC-normalized at the derivation boundary, so that canonically-equivalent Unicode spellings derive the same identifier. The acoustic realization's \emph{tuning reference} is always part of intrinsic identity, including the \texttt{TuningReference::Inherit} case: \texttt{Inherit} is encoded as a distinct presence marker, not as the absence of a field. Consequently a synthetic pitch that inherits its tuning and one that names an explicit tuning are \emph{distinct} identities even when they would resolve to the same frequency in context. This makes synthetic-pitch identity stable under later changes to the inherited tuning environment, and aligns Invariant~11's uniqueness check with a fixed, context-free preimage. \end{requirement} \subsection{Identifier Stability} \begin{requirement} An identifier, once assigned, \MUST{} never be reassigned, even after deletion of the corresponding object. Stale identifiers encountered in stored references \MUST{} be detected and reported by re-anchoring rules (see Chapter~\ref{ch:semops}); they \MUSTNOT{} silently match a different object. \end{requirement} \section{The Event Arena} \label{sec:graph:arena} Events are the rhythmic atoms of the score: pitched events, unpitched events, rests, indeterminate events, trajectories, graphic events, and cues. All events of all kinds in a score are stored in a single arena. \begin{lstlisting}[language=Rust] pub struct EventArena { /// Storage backing for events. Implementation-defined; the /// observable contract is O(1) lookup by EventId and stable /// addresses for the lifetime of the event. storage: ArenaStorage, /// Auxiliary index for fast time-range queries. time_index: EventTimeIndex, } \end{lstlisting} \begin{requirement} Event lookup by \texttt{EventId} \MUST{} be $O(1)$ amortized. Implementations \MAY{} use any storage layout meeting this contract; vector-of-events with stable indices, slot-allocator schemes, and generational arenas are all acceptable. \end{requirement} \subsection{The Event Type} \begin{lstlisting}[language=Rust] pub enum Event { Pitched(PitchedEvent), Unpitched(UnpitchedEvent), Rest(Rest), Indeterminate(IndeterminateEvent), Trajectory(TrajectoryEvent), Graphic(GraphicEvent), Cue(CueEvent), } \end{lstlisting} Every event variant carries: \begin{itemize} \item Its own \texttt{EventId}. \item Its \emph{voice membership}: a \texttt{VoiceId} identifying the unique voice that owns it. \item Its \emph{position} within that voice (either a \texttt{MusicalPosition} or \texttt{WallClockTime}, depending on the enclosing region's time model). \end{itemize} Voice membership and position are stored on the event so that operations addressing an event can resolve its context without traversing parent containers. \subsubsection{Event Position and Duration} \label{sec:graph:event-time} An event's temporal coordinates use union types matching its enclosing region's time model. Position is unioned over musical and wall-clock forms; duration is unioned over musical, wall-clock, and indeterminate forms. \begin{lstlisting}[language=Rust] pub enum EventPosition { Musical(MusicalPosition), WallClock(WallClockTime), } pub enum EventDuration { Musical(MusicalDuration), WallClock(WallClockDuration), Indeterminate(DurationBounds), } /// Bounded interval expressing an indeterminate duration. Bounds are /// concrete (not themselves indeterminate); this prevents recursive /// indeterminacy in the type system. pub struct DurationBounds { pub lower: Option, pub upper: Option, } pub enum ConcreteDuration { Musical(MusicalDuration), WallClock(WallClockDuration), } \end{lstlisting} \begin{requirement} Event position and duration variants \MUST{} agree with the time model of the enclosing region: \begin{itemize} \item Metric regions accept \texttt{EventPosition::Musical} and \texttt{EventDuration::Musical} only. \item Proportional regions accept \texttt{EventPosition::WallClock} and \texttt{EventDuration::WallClock} only. \item Aleatoric regions \MAY{} accept any combination consistent with the region's declared anchoring discipline (see Section~\ref{sec:graph:aleatoric-discipline}). \end{itemize} Implementations \MUST{} reject events whose coordinate variants contradict their enclosing region's time model. \end{requirement} \begin{requirement} If both bounds of a \texttt{DurationBounds} are present, they \MUST{} use the same \texttt{ConcreteDuration} variant, unless the enclosing aleatoric region's anchoring discipline explicitly permits mixed-kind bounds. \end{requirement} \subsubsection{Identified Pitches} \label{sec:graph:identified-pitch} Pitches embedded in events are wrapped in \texttt{IdentifiedPitch} records that pair each pitch with a stable \texttt{PitchId} (Section~\ref{sec:pitch:ids}). The identifier enables spelling attachments, respelling operations, pitch-level reduction rules under concurrent edits, and stable tie pairing through chord reordering. \begin{lstlisting}[language=Rust] pub struct IdentifiedPitch { pub id: PitchId, pub pitch: Pitch, } \end{lstlisting} \begin{requirement} Every pitch embedded in an event \MUST{} be wrapped in an \texttt{IdentifiedPitch}. The contained \texttt{PitchId} \MUST{} be unique within the score's pitch-identity index. Pitch identifiers \MUST{} be minted atomically with their containing event: the operation creating an event mints \texttt{EventId} and the \texttt{PitchId}s of all embedded pitches in a single causal step. \end{requirement} \subsubsection{Pitched Events} \begin{lstlisting}[language=Rust] pub struct PitchedEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// One or more identified pitches. A single pitch is a /// single-note event; multiple pitches are a chord. pub pitches: Vec, /// Articulations attached directly to the event. pub articulations: Vec, /// Single-event dynamic marking (e.g., sfz, fp). Spanning /// dynamics are cross-cutting structures, not event fields. pub dynamic: Option, /// Ornaments attached to the event. Ornament resolutions (the /// realized notes of a trill, mordent, etc.) are cross-cutting /// structures. pub ornaments: Vec, /// Stem configuration: direction, length adjustment, hidden. pub stem: StemConfiguration, /// Whether this event is a grace note, and if so its kind. pub grace: Option, } pub enum GraceKind { Acciaccatura, Appoggiatura, Unmeasured, MeasuredFraction(MusicalDuration), } \end{lstlisting} \begin{requirement} A \texttt{PitchedEvent} \MUST{} have at least one pitch. Empty pitch lists are forbidden; use \texttt{Rest} for the no-pitch case. \end{requirement} \subsubsection{Unpitched Events} \begin{lstlisting}[language=Rust] pub struct UnpitchedEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// Staff line position for the unpitched event (e.g., snare /// drum at the middle line of a 5-line staff, or position 0 /// for a single-line staff). pub staff_position: StaffPosition, /// The unpitched instrument identifier, for sound mapping. pub instrument_member: UnpitchedMemberId, pub articulations: Vec, pub dynamic: Option, pub stem: StemConfiguration, pub grace: Option, } \end{lstlisting} \subsubsection{Rests} \begin{lstlisting}[language=Rust] pub struct Rest { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// Optional explicit vertical positioning. If None, the engraver /// chooses based on voice and meter context. pub vertical_position: Option, /// Whether this rest is visible. Invisible rests preserve timing /// but do not render. pub visible: bool, } \end{lstlisting} \subsubsection{Indeterminate Events} \begin{lstlisting}[language=Rust] pub struct IndeterminateEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// What aspect of the event is indeterminate. pub indeterminacy: IndeterminacyKind, /// Optional hints constraining the indeterminacy. pub hints: IndeterminacyHints, } pub enum IndeterminacyKind { /// "Any pitch in this range/contour." Pitch, /// "Any duration within these bounds." (Realized via /// EventDuration::Indeterminate on the event itself.) Duration, /// "Any of these alternatives." Choice, /// Combination of multiple indeterminacies. Compound(Vec), } pub struct IndeterminacyHints { pub pitch_range: Option, pub pitch_contour: Option, pub density: Option, pub textual_instruction: Option, pub duration_bounds: Option, pub alternatives: Vec, } \end{lstlisting} \subsubsection{Trajectory Events} A trajectory event represents a continuous pitch motion: a glissando, portamento, or pitch bend. \begin{lstlisting}[language=Rust] pub struct TrajectoryEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// Start and end pitch endpoints. Either may reference another /// event's pitch by id, or may carry an explicit identified /// pitch local to the trajectory. pub start: TrajectoryEndpoint, pub end: TrajectoryEndpoint, /// Shape of the trajectory between endpoints. pub shape: TrajectoryShape, /// Visual representation. pub display: TrajectoryDisplay, } pub enum TrajectoryEndpoint { /// Reference to a pitch belonging to another event. EventPitch(PitchId), /// An explicit identified pitch local to this trajectory. /// Such pitches participate in spelling, identity, and editing /// the same way embedded pitches do. ExplicitPitch(IdentifiedPitch), } pub enum TrajectoryShape { Linear, Exponential, Curve(Vec), Stepwise(Vec), } \end{lstlisting} \subsubsection{Graphic Events} A graphic event is an event whose primary content is graphic (drawn or constructed) rather than notated through the standard staff system. Used within hybrid and free-graphic regions for events that nevertheless occupy a rhythmic slot in a voice. \begin{lstlisting}[language=Rust] pub struct GraphicEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// Reference to the graphic object(s) realizing this event. pub graphics: Vec, /// Optional playback parameter bindings (see /// Section~\ref{sec:graph:timebinding}). pub playback_bindings: Vec, } \end{lstlisting} \subsubsection{Cue Events} A cue is a small-print rendering of music from another voice or instrument, displayed as a guide. \begin{lstlisting}[language=Rust] pub struct CueEvent { pub id: EventId, pub voice: VoiceId, pub position: EventPosition, pub duration: EventDuration, /// The source events being cued. pub source: Vec, /// Rendering hints: how the cue should be drawn (size, label, /// abbreviation of the source instrument's name, etc.). pub rendering: CueRendering, } \end{lstlisting} \section{The Canvas} \label{sec:graph:canvas} The canvas is the spatial root of the score. It contains regions. The score root and the canvas are \emph{structural givens}, not operation products (ratified Pass~12): genesis is the creation of an empty score together with its bundle, outside the operation set, and neither object is ever minted, addressed, or deleted by an operation (there is no \texttt{TypedObjectId} kind for either). This is deliberate --- a document that could mint its own root would admit a genesis race under concurrent editing, and every operation would need a defined semantics against a not-yet-existing root. The decision may be revisited only if an addressable multi-canvas model is adopted at a future schema major. \begin{lstlisting}[language=Rust] pub struct Canvas { /// Regions in this canvas. Each region declares its time and /// content models and occupies a time and staff extent. pub regions: Vec, /// Canvas-level configuration: paper size, margins, default /// system layout, page-break behavior. pub layout_defaults: CanvasLayoutDefaults, } /// Canvas-level layout defaults. Page size and margins are in staff /// spaces; a solver reads them as its default page geometry and MAY /// override per solve. The type is defined with schema major 1 (its field /// was named on the canvas from the first draft; only its type was /// undefined). pub struct CanvasLayoutDefaults { pub page_size: CanvasSize, pub margins: CanvasMargins, } /// A page size in staff spaces (1 staff space = staff height / 4). pub struct CanvasSize { pub width: f64, pub height: f64, } /// Page margins in staff spaces. pub struct CanvasMargins { pub top: f64, pub right: f64, pub bottom: f64, pub left: f64, } \end{lstlisting} The default is \textbf{A4 portrait at an 8\,mm staff} (1 staff space = 2\,mm): page $105 \times 148.5$ staff spaces, $7.5$-staff-space margins, hence a $90 \times 133.5$ content area. Each staff-space scalar is realized canonically as a \texttt{CanonicalF64} leaf; the reference engraver maps a \texttt{CanvasLayoutDefaults} onto its internal page geometry. This closes the long-standing gap in which the canvas named \texttt{layout\_defaults} but no chapter defined \texttt{CanvasLayoutDefaults}. \subsection{Regions} \begin{lstlisting}[language=Rust] pub struct Region { pub id: RegionId, /// Time model: metric, proportional, or aleatoric. pub time_model: RegionTimeModel, /// Content model: staff-based, free graphic, or hybrid. pub content: RegionContent, /// Range in time that this region occupies. pub time_extent: TimeExtent, /// Range in vertical staff space that this region occupies. /// A region may cover all staves (full-score region) or a subset /// (a free-graphic gesture occupying only one staff's vertical /// band, for example). pub staff_extent: StaffExtent, /// Optional tempo and tuning overrides scoped to this region. pub local_tempo_map: Option, pub local_tuning_overrides: Vec, /// Whether spanners (slurs first of all) may cross this region's /// boundary into an adjacent region. Default false: cross-region /// spanning is not permitted unless a region opts in. Added with /// schema major 1. pub permits_spanning_slurs: bool, } \end{lstlisting} For a slur (or other spanner) whose endpoints lie in \emph{different} regions, permission is governed conjunctively (ratified Pass~12): the boundary is permeable only when \emph{both} endpoint regions set \texttt{permits\_spanning\_slurs} --- a region that forbids spanning is never crossed against its declaration, regardless of which side the spanner starts on. The check is an authoring-time advisory only; it never alters canonical reduction. \begin{lstlisting}[language=Rust] pub enum RegionContent { /// Staff-based notation: one or more staves carrying voices and /// events. StaffBased(StaffBasedContent), /// Free graphic content: no staves, only graphic objects placed /// in the region's coordinate space. FreeGraphic(GraphicContent), /// Hybrid: staves with overlaid graphic content. The graphic /// layer is rendered above (or below, configurably) the staff /// layer. Hybrid { staves: StaffBasedContent, overlay: GraphicContent, overlay_below_staves: bool, }, } pub struct TimeExtent { pub start: TimeAnchor, pub end: TimeAnchor, } pub struct StaffExtent { /// Identifiers of the globally-identified staves whose content /// this region carries. A region carries one StaffInstance per /// listed StaffId. The same StaffId may appear in the /// staff_extent of adjacent regions; this is the mechanism by /// which a staff's identity persists across regions. pub staves: Vec, } \end{lstlisting} \subsection{Region Overlap and Concurrency} \begin{requirement} Regions \MAY{} overlap in time if their staff extents are disjoint. Regions \MUSTNOT{} overlap in both time and staff extent. The graph's construction \MUST{} reject configurations violating this constraint. \end{requirement} \begin{rationale} Concurrent regions on disjoint staves are essential for the hybrid notation feature: a metric staff and a proportional staff sounding simultaneously are two regions with overlapping time extents but disjoint staff extents. Overlapping time \emph{and} staff would mean two regions claim the same content, which is ambiguous. \end{rationale} \subsection{Staff-Based Content} A staff-based region carries one or more staff \emph{instances}, each of which manifests a globally-identified staff for the duration of the region. The score's metric organization is per-staff (admitting polymeter); barline alignment between staves is recorded as an explicit relationship when meters coincide. \begin{lstlisting}[language=Rust] pub struct StaffBasedContent { /// Staff instances appearing in this region. Each references a /// globally-identified Staff and provides region-local content. pub staff_instances: Vec, /// Default metric grid for this region. Staves without their own /// local_metric_grid inherit this grid. None means the region /// has no default metric organization (acceptable for transitional /// regions or for regions where every staff declares its own). pub default_metric_grid: Option, /// Explicit barline alignment groups: places where measures /// across multiple staves are declared to coincide. Used for /// engraving barlines that span multiple staves under polymeter /// and for analytical purposes. Implementations MAY derive /// some alignment groups automatically from coincident measure /// starts; explicit groups are authoritative. pub barline_alignment_groups: Vec, /// System breaks declared by the user. The engraver may /// override these if region settings permit (see Chapter 6). pub user_system_breaks: Vec, /// Page breaks declared by the user. Same override rule. pub user_page_breaks: Vec, } /// A region-local manifestation of a globally-identified Staff. pub struct StaffInstance { pub id: StaffInstanceId, /// The globally-identified staff this instance manifests. pub staff: StaffId, /// Voices appearing on this staff instance. pub voices: Vec, /// Clef changes within this instance. pub clef_sequence: Vec, /// Key signature changes within this instance. pub key_sequence: Vec, /// Optional local metric grid: if Some, overrides the region's /// default_metric_grid for this staff (polymetric case). /// If None, the staff inherits the region's default_metric_grid. pub local_metric_grid: Option, /// Measures belonging to this staff instance, in order. Measures /// are per-staff (admitting polymeter); alignment across staves /// is recorded in barline_alignment_groups. pub measures: Vec, /// Optional per-instance instrument override. None means inherit /// from the global Staff's declared instrument. pub instrument_override: Option, /// Optional per-instance staff line configuration override. /// None means inherit from the global Staff's default. pub staff_lines_override: Option, /// Whether this instance is visible. Hidden instances preserve /// content but are not rendered (used for empty-staff suppression). pub visible: bool, } /// The clef content model (ratified schema major 2; the value types /// landed with the visible slice and supersede the earlier ClefId /// identifier sketch). A clef is a value, not a registry reference. pub enum ClefShape { /// G clef (treble family) -- reference pitch G4. G, /// F clef (bass family) -- reference pitch F3. F, /// C clef (alto / tenor family) -- reference pitch middle C (C4). C, /// Unpitched percussion clef -- no diatonic reference. Percussion, } pub struct Clef { pub shape: ClefShape, /// The staff line (1 = bottom) the shape's reference pitch sits on. pub line: i8, /// Signed octave displacement (e.g., -1 for the vocal tenor G clef). pub octave_shift: i8, } /// A key signature on the circle of fifths, validated to the /// conventional CMN range -7 (seven flats) ..= +7 (seven sharps). /// Constructed only through a checked constructor; decoders reject /// out-of-range values. pub struct KeySignature { fifths: i8, // private; KeySignature::new rejects |fifths| > 7 } /// A clef change at a point in a staff instance. pub struct ClefChange { pub anchor: TimeAnchor, pub clef: Clef, } /// A key-signature change at a point in a staff instance. pub struct KeySignatureChange { pub anchor: TimeAnchor, pub key: KeySignature, } /// A per-staff (or per-region-default) metric organization. pub struct MetricGrid { /// Sequence of meter changes within the scope of this grid. pub meter_sequence: Vec, } pub struct MeterChange { /// Where this meter takes effect, expressed as an anchor within /// the enclosing staff or region. pub anchor: TimeAnchor, /// The active time signature from this point until the next /// MeterChange or the end of the grid's scope. pub time_signature: TimeSignatureId, } /// A declaration that the measure boundaries of two or more staff /// instances coincide at a particular point. pub struct BarlineAlignmentGroup { pub id: BarlineAlignmentGroupId, /// The staff instances participating in this alignment, paired /// with the measures whose boundaries coincide. pub members: Vec, } pub struct BarlineAlignmentMember { pub staff_instance: StaffInstanceId, pub measure: MeasureId, pub position: MeasurePosition, } pub struct Measure { pub id: MeasureId, pub start: TimeAnchor, pub time_signature: Option, pub explicit_number: Option, pub number_visibility: MeasureNumberVisibility, } \end{lstlisting} \begin{requirement} Measures \MUST{} belong to a single \texttt{StaffInstance}, not to the enclosing region. This admits polymeter: different staves in the same region \MAY{} carry different meter sequences and consequently different measure boundaries. Barline coincidences across staves \MUST{} be expressed via \texttt{BarlineAlignmentGroup} entries when explicit alignment is authoritative; otherwise alignment is derived at engraving time from coincident measure-start positions. \end{requirement} \section{Staves, Voices, and Instruments} \label{sec:graph:staves} \subsection{Instruments} An instrument is an abstract definition of a sounding entity. \begin{lstlisting}[language=Rust] pub struct Instrument { pub id: InstrumentId, pub name: String, pub abbreviation: Option, /// MIDI program, sound library mapping, or unpitched instrument /// definitions. The structure is detailed in the audio engine /// specification (out of scope here); the core stores the /// configuration opaquely. pub sound_config: SoundConfiguration, /// Default transposition (e.g., B-flat clarinet sounds a major /// second below written). pub transposition: Option, /// Default playable range. Engraving may flag out-of-range /// pitches; not a hard constraint. pub range: Option, /// Default clef and staff line configuration for staves of this /// instrument. The clef is the content-bearing Clef value defined /// in this chapter (shape, line, octave shift) -- the value model /// ratified with the visible slice supersedes the earlier ClefId /// identifier sketch (reconciled schema major 2). pub default_clef: Clef, pub default_staff_lines: StaffLineConfiguration, /// For unpitched instruments: definitions of each playable /// member (snare, kick, ride bell, etc.) and their staff /// positions. pub unpitched_members: Vec, } /// Opaque sound configuration (defined schema major 2). The audio /// engine specification owns the structure; the core stores the bytes /// verbatim and never interprets them. Empty is the default. pub struct SoundConfiguration(pub Vec); /// A written-versus-sounding transposition (defined schema major 2). /// Structural only: the diatonic and chromatic step counts of the /// interval (e.g., B-flat clarinet = -1 diatonic, -2 chromatic). /// Semantically ADVISORY until the Chapter 4 tuning catalog pins /// interval algebra (the same P12-K2 pinning discipline as the /// Transpose operation); nothing in the core resolves it to /// frequencies or respells through it yet. pub struct TranspositionInterval { pub diatonic_steps: i32, pub chromatic_steps: i32, } /// One playable member of an unpitched instrument (defined schema /// major 2): the identifier UnpitchedEvent.instrument_member /// references, its display name, and its default staff position. pub struct UnpitchedMember { pub member: UnpitchedMemberId, pub name: String, pub staff_position: StaffPosition, } \end{lstlisting} \texttt{UnpitchedMemberId} is an \emph{instrument-scoped small value} (a plain 32-bit integer), not a member of the 128-bit typed-identifier family --- it is minted by the author of the instrument definition, not by replicas, and participates in no content derivation. Member values \emph{should} be unique within one instrument's \texttt{unpitched\_members}; the authoring advisory layer flags a duplicate, and resolution is by first match in list order (canonical bytes preserve the authored order), so a duplicate is deterministic, merely shadowed. An \texttt{UnpitchedEvent.instrument\_member} with no matching entry resolves to \emph{no member definition}: the event still renders (its own \texttt{staff\_position} field governs placement --- the member's \texttt{staff\_position} is the \emph{authoring default} copied onto new events, not a render-time override), and sound mapping falls back to the instrument's \texttt{sound\_config}. This no-match tolerance is load-bearing: every pre-major-2 instrument has an empty member list while its events carry member values. \texttt{StaffPosition}'s vertical convention (which integer is which line, and its orientation) remains \emph{deliberately deferred} to the Chapter~\ref{ch:layout-ir} vertical model, with the unpitched-rendering tranche as its landing site; until then the field is preserved and compared verbatim, and no implementation may claim conformant unpitched \emph{rendering}. \subsection{Staves: Identity Versus Instance} \label{sec:graph:staff-identity} The specification distinguishes two staff concepts: \begin{description} \item[\texttt{Staff}] is the \emph{global, abstract} staff identity persisting across the entire score. A staff is the thing a performer is reading: the "Flute 1" staff, the "Piano upper staff." A staff is declared once in the score and is referenced by staff instances in any region where it appears. \item[\texttt{StaffInstance}] is the \emph{region-local manifestation} of a staff: the voices, measures, clef changes, key changes, and metric grid that belong to that staff for the duration of one region. A single \texttt{Staff} may have multiple \texttt{StaffInstance}s across the score; each instance belongs to exactly one region. \end{description} \begin{lstlisting}[language=Rust] pub struct Staff { pub id: StaffId, /// Human-readable name (e.g., "Flute 1", "Violin II"). pub name: String, /// Optional abbreviated name for subsequent systems. pub abbreviation: Option, /// The instrument this staff renders by default. May be /// overridden per-instance. pub instrument: InstrumentId, /// Default clef for new instances of this staff (the /// content-bearing Clef value defined in this chapter; reconciled /// schema major 2). pub default_clef: Clef, /// Default staff line configuration. pub default_staff_lines: StaffLineConfiguration, /// Visual grouping: which staff group (if any) this staff /// belongs to (e.g., piano grand staff, choral group). pub group: Option, } pub struct StaffLineConfiguration { pub line_count: u8, pub line_spacing: SpaceUnit, pub line_style: LineStyle, pub bracket: Option, } /// A dimension in staff spaces (defined schema major 2). Staff line /// spacing is relative to the global staff space; 1.0 is a /// normal-size staff, smaller values yield cue/ossia staves. pub struct SpaceUnit(pub CanonicalF64); /// A line drawing style (defined schema major 2). Shared by staff /// lines and the slur/tie/spanner style records below. pub enum LineStyle { Solid, Dashed, Dotted, } /// A per-staff bracket adornment (defined schema major 2), distinct /// from StaffGroup-level bracketing. pub enum StaffBracketKind { Brace, Bracket, } \end{lstlisting} The score's top-level structure carries a list of \texttt{Staff} objects alongside its list of \texttt{Instrument} objects; each region's staff instances reference these by \texttt{StaffId}. \begin{requirement} Every \texttt{StaffInstance.staff} \MUST{} resolve to a \texttt{Staff} declared at the score level. A single \texttt{StaffId} \MAY{} be referenced by multiple \texttt{StaffInstance}s in different regions: this is how a staff maintains continuous identity across the score's regions. A \texttt{StaffInstance} \MUST{} belong to exactly one region. The region's \texttt{staff\_instances} list \MUST{} contain the instance, and the instance's content is owned by that region. Continuous attachments (e.g., key signatures, clef changes, metric grids) \MAY{} appear on multiple staff instances of the same \texttt{Staff} when those instances are adjacent in time; the engraving pipeline reconstructs continuous staff appearance from per-instance content. \end{requirement} \subsection{Voices} A voice is a polyphonic line within a staff instance. Voices hold ordered references to events; the events themselves live in the score's arena. \begin{lstlisting}[language=Rust] pub struct Voice { pub id: VoiceId, /// Ordered references to events in this voice, sorted by /// position. Each event has voice == this voice's id. pub events: Vec, /// Default stem direction for this voice. Common conventions: /// voice 1 stems up, voice 2 stems down on the same staff. pub default_stem_direction: Option, /// Whether this voice is the staff's primary voice (for purposes /// of rest placement, beam direction defaults, and similar /// engraving decisions). pub is_primary: bool, /// Provenance of this voice: user-declared, imported, or /// system-promoted by concurrent-edit conflict resolution. pub origin: VoiceOrigin, } pub enum VoiceOrigin { /// Created by an explicit user action. UserDeclared, /// Imported from a foreign format. Imported { format: ForeignFormatId }, /// System-promoted to resolve a concurrent-edit collision. /// The original_voice is the voice into which the user had /// intended to insert; the promoted voice carries the event /// authored by losing_operation. SystemPromoted { winning_operation: OperationId, losing_operation: OperationId, original_voice: VoiceId, }, } \end{lstlisting} \begin{requirement} The number of voices per staff instance \MUST{} be unbounded in the data model. Engraving heuristics (Chapter~\ref{ch:layout-ir} and beyond) may flag visual issues with high voice counts; the graph admits any number. \end{requirement} \begin{requirement} Every event \MUST{} belong to exactly one voice. Voice membership is stored on the event (the \texttt{voice} field) and \MUST{} agree with the membership of the voice's \texttt{events} list: for any event $e$ in voice $v$'s events list, $e\texttt{.voice}$ \MUST{} equal $v\texttt{.id}$. \end{requirement} \subsubsection{System-Promoted Voices} \label{sec:graph:promoted-voices} When concurrent operations cause an \texttt{InsertEvent} collision that cannot be resolved by ordering alone (because both events have positive duration and would overlap within the target voice), the deterministically-losing event is placed in a newly allocated voice with \texttt{origin: VoiceOrigin::SystemPromoted}. This preserves the non-overlap invariant without rejecting user intent. \begin{requirement} The identifier of a system-promoted voice \MUST{} be derived deterministically from a fixed function of: \begin{enumerate} \item The enclosing staff instance's \texttt{StaffInstanceId}. \item The original target voice's \texttt{VoiceId}. \item The winning operation's \texttt{OperationId}. \item The losing operation's \texttt{OperationId}. \end{enumerate} The derivation is the system-derived identifier function of Section~\ref{sec:graph:system-derived} with domain tag \texttt{"MUSCSVCE"} over a fixed 64-byte preimage formed by concatenating the four identifiers in the order listed above, each as its 16-byte big-endian canonical form: \begin{lstlisting}[language=Rust] fn derive_promoted_voice_id( staff_instance: StaffInstanceId, original_voice: VoiceId, winning_op: OperationId, losing_op: OperationId, ) -> VoiceId { // 4 * 16 = 64 bytes, each component big-endian. let mut inputs = Vec::with_capacity(64); inputs.extend_from_slice(&staff_instance.canonical_bytes()); inputs.extend_from_slice(&original_voice.canonical_bytes()); inputs.extend_from_slice(&winning_op.canonical_bytes()); inputs.extend_from_slice(&losing_op.canonical_bytes()); derive_system_id::(b"MUSCSVCE", &inputs) } \end{lstlisting} The enclosing staff instance is recovered from graph containment at reduction time; the operation does not store it redundantly. Determinism is required so that all replicas independently arrive at the same promoted voice identity. This derivation is the same one whose result Invariant~18 (Section~\ref{sec:graph:invariants}) verifies; the reducer that mints the voice and the verifier that checks it consume one derivation. \end{requirement} \begin{requirement} \label{req:graph:promotion-generalization} The promotion rule generalizes from the pairwise case to any number of concurrent overlapping inserts into the same voice by an order-independent pre-pass: \begin{enumerate} \item Bucket the concurrent \texttt{InsertEvent} operations by their target \texttt{(staff\_instance, original\_voice)}. \item Within a bucket, walk the operations in ascending \texttt{OperationId} order, maintaining a \emph{retained set} of mutually non-overlapping inserts that stay in the original voice. \item An operation whose inserted interval does not overlap any member of the retained set joins the retained set. An operation whose interval overlaps some retained member is a \emph{loser} and is promoted; the lowest-\texttt{OperationId} retained member it overlaps is its \texttt{winning\_operation}. \end{enumerate} Because the walk is by \texttt{OperationId} and the retained set is built deterministically, every replica promotes the same operations and derives the same promoted-voice identities regardless of delivery order. ``Overlap'' is interval overlap of the half-open inserted spans \texttt{[start,\,end)}: the rule applies to \emph{partial} overlaps, not only to inserts that share an identical start position. This is a deliberate widening of the pairwise rule's identical-start-position language; it is the correct reading because any positive-duration overlap breaks the voice non-overlap invariant (Invariant~3), not just a shared onset. \end{requirement} \begin{requirement} System-promoted voices are first-class graph objects: they appear in the staff instance's \texttt{voices} list, they participate in cross-cutting structures, and they are visible to users. They are \emph{not} hidden or transient. The layout pipeline (Chapter~\ref{ch:layout-ir}) \SHOULD{} provide a visual indication that a voice was system-promoted until the user normalizes it (via a later \texttt{NormalizePromotedVoice} or \texttt{MergeVoices} operation, specified in the operation catalog). Promoted voices \MUSTNOT{} silently become user-authored voices; doing so would convert a CRDT conflict resolution into musical authorship without the user's consent. \end{requirement} \begin{requirement} Within a voice, events \MUST{} be sorted by position and \MUSTNOT{} overlap in time. Concurrent material on the same staff \MUST{} be expressed via multiple voices. \end{requirement} \section{Cross-Cutting Structures} \label{sec:graph:cross} Cross-cutting structures hold references into the tree (typically event identifiers) and represent musical phenomena that span multiple events. The registry is partitioned by type for both type safety and fast indexed lookup. \begin{lstlisting}[language=Rust] pub struct CrossCuttingRegistry { pub slurs: Vec, pub ties: Vec, pub beams: Vec, pub tuplets: Vec, pub spanners: Vec, pub markers: Vec, pub repeats: Vec, pub analytical: Vec, pub comments: Vec, pub graphic_gestures: Vec, pub lyrics: Vec, pub chord_symbols: Vec, } \end{lstlisting} \subsection{Slurs and Phrase Marks} \label{sec:graph:slurs} \begin{lstlisting}[language=Rust] pub struct Slur { pub id: SlurId, /// First and last events under the slur. pub start_event: EventId, pub end_event: EventId, /// Slur kind: legato slur, phrase mark, articulation slur, /// editorial. pub kind: SlurKind, /// Optional curvature override. The engraver computes default /// curvature; this field overrides it. pub curvature_override: Option, /// Visual style: solid, dashed, dotted; line thickness curve. pub style: SpanStyle, } /// The class of a slur (defined schema major 2). The v1-to-v2 /// migration default is Legato. pub enum SlurKind { /// An ordinary legato slur. Legato, /// A phrase mark (typically longer, over sub-phrases). Phrase, /// An articulation slur (e.g., over a two-note sigh figure). Articulation, /// An editorial slur (rendered distinctly, e.g., dashed). Editorial, } /// Which side of the notes a curve arcs toward (defined schema /// major 2). pub enum CurveDirection { Above, Below, } /// An authored curvature override (defined schema major 2). The /// engraver computes default curvature; each present field overrides /// that component of it. Consumed by the Standard engraving tier; /// stored and preserved at every tier. pub struct CurvatureOverride { pub direction: Option, /// Arc height at the apex. pub height: Option, } /// The visual style of a spanning mark (defined schema major 2): one /// shared record for Slur.style, Tie.style, and Spanner.style. /// Defaults: solid, engraver-chosen thickness. pub struct SpanStyle { pub line: LineStyle, /// Line thickness; None = the engraver's default. pub thickness: Option, } \end{lstlisting} \subsection{Ties} \label{sec:graph:ties} \begin{lstlisting}[language=Rust] pub struct Tie { pub id: TieId, /// The two events being tied. The end event's pitch(es) must /// match the corresponding pitches in the start event. pub start_event: EventId, pub end_event: EventId, /// For chord ties: which pitches in the start event are tied /// to which in the end event, expressed by stable PitchId pairs. /// None means all pitches are tied by enharmonic matching in /// pitch-id-ascending order. pub pitch_pairing: Option>, /// Tie class: determines the validation profile applied to this /// tie. The default Standard class requires same-voice immediate /// adjacency; other classes relax the rule for editorial, /// cross-voice, or notation-specific use. pub class: TieClass, /// Visual style (the shared SpanStyle record, schema major 2). pub style: SpanStyle, } pub enum TieClass { /// Standard CMN tie: same voice, immediate adjacency in voice /// order, pitches enharmonically equivalent. Standard, /// Editorial tie: connects pitches the editor considers /// continuous, spanning intervening rests or invisible events. /// Drawn with editorial style (dashed or bracketed) by default. Editorial, /// Cross-voice tie: connects pitches across voice boundaries /// within the same staff instance. Used for keyboard divisi, /// re-voicing across hands, and similar constructions. CrossVoice, /// Laissez-vibrer tie: a tie with no defined end event, drawn /// trailing from the start event. The end_event field is /// permitted to equal the start_event for this class, with /// pitch_pairing self-referential. LaissezVibrer, /// Registered class with custom validation behavior, defined /// by a notation grammar plugin. Registered(TieClassRegistryId), } \end{lstlisting} \begin{rationale} Pairing by stable \texttt{PitchId} rather than by positional index preserves tie identity through chord-reordering edits and through CRDT-style concurrent modification of the chord's pitch list. Index-based pairing would silently retarget when pitches were inserted, deleted, or reordered within a chord. The tie class permits common editorial and contemporary-notation practices (editorial dashed ties spanning rests, cross-voice ties in keyboard scores, laissez-vibrer ties without a notated end) to be expressed without relaxing the structural invariants of the standard tie. \end{rationale} \begin{requirement} Tie validation is class-specific: \begin{itemize} \item \texttt{TieClass::Standard}: the start and end events \MUST{} be in the same voice; the start event \MUST{} immediately precede the end event in that voice's event sequence; the paired pitches \MUST{} be enharmonically equivalent under the active tuning system. \item \texttt{TieClass::Editorial}: the start and end events \MUST{} be in the same voice and the start \MUST{} precede the end in voice order, but intervening events are permitted. Paired pitches \MUST{} be enharmonically equivalent. \item \texttt{TieClass::CrossVoice}: the start and end events \MAY{} be in different voices on the same staff instance. The start's resolved position \MUST{} be less than or equal to the end's. Paired pitches \MUST{} be enharmonically equivalent. \item \texttt{TieClass::LaissezVibrer}: the end event \MAY{} equal the start event, in which case the tie has no notated destination. No adjacency requirement applies. \item \texttt{TieClass::Registered}: validation is defined by the registered class's contract; the registry \MUST{} specify adjacency and pitch-equivalence requirements for the class. \end{itemize} \end{requirement} \subsection{Beams} \begin{lstlisting}[language=Rust] pub struct Beam { pub id: BeamId, pub events: Vec, pub level: u8, // primary beam level /// Sub-beam structure for inner subdivisions. Each entry /// describes a beam segment at a deeper level. pub sub_beams: Vec, /// Beam angle and stem-length overrides. pub geometry_override: Option, } /// A beam segment at a deeper subdivision level (defined schema /// major 2): a contiguous subset of the owning beam's events beamed /// together at `level` (strictly deeper than the owning beam's /// primary level). pub struct SubBeam { pub level: u8, pub events: Vec, } /// An authored beam-geometry override (defined schema major 2). Each /// present field overrides the engraver's computed geometry. Consumed /// by the Standard engraving tier; stored and preserved at every tier. pub struct BeamGeometryOverride { /// Beam slope: staff spaces of rise per staff space of run /// (dimensionless, hence not a SpaceUnit). pub slope: Option, /// Vertical displacement of the beam from its default placement /// (positive = up). pub offset: Option, } \end{lstlisting} \subsection{Spanners} A spanner is a generic spanning mark: hairpins, octave lines, pedal lines, trill extensions, and similar. \begin{lstlisting}[language=Rust] pub struct Spanner { pub id: SpannerId, pub kind: SpannerKind, pub start: TimeAnchor, pub end: TimeAnchor, /// Which staff or staves this spanner attaches to. Most spanners /// target a single staff; some (system-level dynamics, pedal /// lines spanning grand staff) target multiple. pub staves: Vec, /// Visual style (the shared SpanStyle record, schema major 2). pub style: SpanStyle, } pub enum SpannerKind { /// An unclassified spanning mark: renders as a plain line or /// bracket. First variant deliberately (schema major 2): it is /// the v1-to-v2 migration default -- a v1 spanner carried no /// kind, and Generic is the honest translation of that absence. Generic, Hairpin(HairpinDirection), OctaveLine(OctaveOffset), PedalLine(PedalKind), TrillExtension, Glissando, Portamento, TextLine(TextLineDefinition), Bracket(BracketKind), } /// Hairpin orientation (defined schema major 2). pub enum HairpinDirection { Crescendo, Diminuendo, } /// Octave-line displacement in signed octaves (defined schema /// major 2): +1 = 8va, -1 = 8vb, +2 = 15ma, -2 = 15mb. Zero is /// representable but degenerate; the authoring advisory layer flags /// it, reduction does not. pub struct OctaveOffset(pub i8); /// Pedal-line kind (defined schema major 2). pub enum PedalKind { Sustain, Sostenuto, UnaCorda, } /// A text line's content (defined schema major 2); the dash pattern /// comes from the spanner's style. pub struct TextLineDefinition { pub text: String, } /// A bracket spanner's shape (defined schema major 2). Growth is by /// appended variant. pub enum BracketKind { Square, } \end{lstlisting} \subsection{Markers} A marker is a point annotation: rehearsal marks, section labels, tempo text, fermatas (which technically attach to events but may also be region-level), and similar. \begin{lstlisting}[language=Rust] pub struct Marker { pub id: MarkerId, pub anchor: TimeAnchor, pub kind: MarkerKind, pub display: MarkerDisplay, } pub enum MarkerKind { Rehearsal(RehearsalMark), Section(SectionLabel), Tempo(TempoMarking), Coda, Segno, DalSegno, DaCapo, Fine, Custom(CustomMarkerId), } \end{lstlisting} \subsection{Repeat Structures} \label{sec:graph:repeats} \begin{lstlisting}[language=Rust] pub struct RepeatStructure { pub id: RepeatStructureId, pub kind: RepeatKind, pub start: TimeAnchor, pub end: TimeAnchor, /// For voltas: which endings apply on which passes. pub voltas: Vec, } pub enum RepeatKind { SimpleRepeat { count: u32 }, DaCapo { end_target: TimeAnchor }, DalSegno { segno: TimeAnchor, end_target: TimeAnchor }, Volta, } /// One volta bracket (defined schema major 2): the passes it applies /// on and the time span it covers. A RepeatStructure of kind Volta /// carries one entry per ending (e.g., a first ending Volta with /// endings = [1] and a second with endings = [2]). pub struct Volta { /// The pass numbers this ending plays on (1-based), ascending. pub endings: Vec, pub start: TimeAnchor, pub end: TimeAnchor, } \end{lstlisting} The \texttt{endings} constraints (non-empty, 1-based, strictly ascending) are \emph{advisory}, on the \texttt{OctaveOffset} pattern: a violating value is representable, decoders and reduction accept it, and the authoring validation layer flags it. Rendering treats an unlistable ending set as a plain bracket. The v1-to-v2 migration default for a kindless v1 repeat structure is \texttt{SimpleRepeat} with \texttt{count}~$=$~2: a v1 \texttt{RepeatStructure} \emph{meant} a repeat, and playing the span twice is the conventional semantics of an unadorned repeat sign. Voltas default empty. \subsection{Analytical Annotations} Analytical annotations record analyses of the music: Roman numerals, form labels, motivic brackets, set-theory analyses, Schenkerian graphs. They are first-class and may appear on dedicated analysis layers. \begin{lstlisting}[language=Rust] pub struct AnalyticalAnnotation { pub id: AnalyticalAnnotationId, pub kind: AnalyticalKind, pub anchor: AnnotationAnchor, /// Optional analysis layer. None means the engraved layer; /// Some(id) targets a specific analysis layer. pub layer: Option, pub content: AnalyticalContent, } pub enum AnalyticalKind { RomanNumeral, FunctionalLabel, SetTheoryLabel, SchenkerianGraph, FormSection, MotivicBracket, Custom(CustomAnalyticalKindId), } pub enum AnnotationAnchor { Event(EventId), Range { start: TimeAnchor, end: TimeAnchor }, Region(RegionId), } \end{lstlisting} \subsection{Comments} Comments are review-mode annotations: discussion threads anchored to points or ranges in the score. \begin{lstlisting}[language=Rust] pub struct Comment { pub id: CommentId, pub anchor: AnnotationAnchor, pub thread: Vec, pub resolved: bool, } pub struct CommentMessage { pub author: AuthorId, pub timestamp: Timestamp, pub body: String, } \end{lstlisting} \subsection{Graphic Gestures} A graphic gesture is a drawn or constructed graphic element that spans events, staves, or regions and is not contained within a single graphic region. Used for performance-direction sweeps, multi-staff curved gestures, and similar. \begin{lstlisting}[language=Rust] pub struct GraphicGesture { pub id: GraphicGestureId, /// The graphic objects composing this gesture. Stored in the /// canvas's graphic-object storage. pub objects: Vec, /// Anchoring: how this gesture is positioned relative to score /// content. pub anchoring: GestureAnchoring, /// Optional playback parameter bindings. pub playback_bindings: Vec, } pub enum GestureAnchoring { /// Anchored to events: the gesture moves with them. Events(Vec), /// Anchored to a time and staff range. Range { start: TimeAnchor, end: TimeAnchor, staves: Vec, }, /// Free canvas coordinates: does not follow score edits. Free(CanvasCoordinates), } \end{lstlisting} \section{Graphic Content} \label{sec:graph:graphic} Free-graphic and hybrid regions carry graphic content: vector primitives, drawn strokes, text, and images. Graphic content is expressed in the region's local coordinate space. \begin{lstlisting}[language=Rust] pub struct GraphicContent { pub objects: Vec, /// The region's local coordinate system. Region transforms /// map this to canvas coordinates. pub coordinate_system: LocalCoordinateSystem, } \end{lstlisting} \subsection{Graphic Objects} \begin{lstlisting}[language=Rust] pub struct GraphicObject { pub id: GraphicObjectId, /// The object's geometric kind. pub kind: GraphicObjectKind, /// Transform applied within the region's coordinate space. pub transform: Transform2D, /// Layer ordering: higher values draw on top of lower. pub layer: i32, /// Visual style: stroke color, fill, line weight, opacity. pub style: GraphicStyle, /// Optional time binding. See Section~\ref{sec:graph:timebinding}. pub time_binding: Option, } pub enum GraphicObjectKind { Path(PathData), Shape(ShapePrimitive), Text(TextData), Image(ImageData), Group(Vec), Stroke(StrokeData), } pub enum ShapePrimitive { Line { start: Point2D, end: Point2D }, Rectangle { origin: Point2D, size: Size2D }, Ellipse { center: Point2D, radii: Size2D }, Polygon(Vec), Bezier(Vec), } pub struct StrokeData { /// Pressure-sensitive stylus stroke samples: position, pressure, /// tilt, and time per sample. pub samples: Vec, } pub struct StrokeSample { pub position: Point2D, pub pressure: f32, // 0.0..=1.0 pub tilt_xy: (f32, f32), // radians pub timestamp: WallClockTime, } \end{lstlisting} \subsection{Coordinates} \begin{requirement} Graphic objects \MUST{} be stored in region-local coordinates. The region's transform maps local coordinates to canvas coordinates. This permits regions to be moved, resized, or otherwise transformed without rewriting the coordinates of every contained object. \end{requirement} \subsection{Time Bindings} \label{sec:graph:timebinding} A graphic object \MAY{} carry an optional time binding, which assigns a temporal meaning to the object beyond its visual appearance. Time bindings are the mechanism by which a drawn curve becomes a performance instruction (a filter sweep, an amplitude envelope, a density trajectory). \begin{lstlisting}[language=Rust] pub struct TimeBinding { /// What musical or wall-clock range this graphic spans. pub time_extent: TimeExtent, /// What parameter, if any, this graphic drives. None means the /// graphic is decorative; Some specifies a named parameter. pub parameter: Option, /// How the graphic's geometry maps to parameter values. pub mapping: ParameterMapping, } pub struct ParameterBinding { pub target: ParameterTarget, pub parameter_name: String, pub value_range: ParameterRange, } pub enum ParameterTarget { /// A specific event's parameter (e.g., this event's velocity). Event(EventId), /// A voice-level parameter (e.g., voice expression CC). Voice(VoiceId), /// A staff-level parameter. Staff(StaffId), /// A region-level parameter. Region(RegionId), /// A score-global parameter (e.g., master gain). Score, } pub enum ParameterMapping { /// Vertical position in region coordinates maps linearly to /// parameter value. VerticalLinear, /// Vertical position with exponential mapping. VerticalExponential, /// Stroke pressure maps to parameter value. StrokePressure, /// Stroke velocity maps to parameter value. StrokeVelocity, /// Custom mapping defined by a plugin. Registered(MappingRegistryId), } \end{lstlisting} \begin{rationale} Time bindings make graphic-notation playback expressible in the core data model without committing to specific audio engine semantics. The core stores what parameter a graphic drives and how its geometry maps to values; the audio engine spec consumes this binding and applies it. Decorative graphics (binding is \texttt{None}) remain pure visual content. \end{rationale} \section{Parts} \label{sec:graph:parts} A part is a per-instrument or per-section view onto the score, intended for printed extraction or focused performer use. Parts live separately from the canvas; they are projections defined by a part-extraction specification. \begin{lstlisting}[language=Rust] pub struct PartDefinition { pub id: PartDefinitionId, /// Human-readable name (e.g., "Flute 1", "Violins II"). pub name: String, /// Which staves are included in this part, in order from top /// to bottom. pub staves: Vec, /// Part-specific layout overrides: page size, system count, /// cue insertions, multirest consolidation, page-turn placement. pub layout_overrides: PartLayoutOverrides, /// Per-event visibility overrides specific to this part (e.g., /// hide a cue in the full score but show it in the part). pub visibility_overrides: Vec, /// Auto-cue insertions: source staves to draw cues from when /// the part has long rests. pub auto_cue_sources: Vec, } \end{lstlisting} \subsection{Parts Are Projections, Not Storage} \begin{requirement} A part \MUSTNOT{} store musical content directly; it \MUST{} consist only of references to score content and overrides. Edits to score content \MUST{} propagate to all parts that reference that content. \end{requirement} \section{Analysis Layers and Views} \label{sec:graph:layers} \subsection{Analysis Layers} Analysis layers, introduced for spelling in Section~\ref{sec:pitch:layers} and used by analytical annotations, are first-class objects on the score. \begin{lstlisting}[language=Rust] pub struct AnalysisLayer { pub id: AnalysisLayerId, pub name: String, pub description: Option, /// Which views display this layer. pub visible_in_views: Vec, } \end{lstlisting} \subsection{Views} A view is a recipe for displaying a configuration of the score: which analysis layers are active, which parts are rendered, what overrides apply. Views are how the system supports condensed scores, analytical views, study scores, and lead-sheet projections. \begin{lstlisting}[language=Rust] pub struct ViewDefinition { pub id: ViewId, pub name: String, pub kind: ViewKind, /// Which analysis layers are active in this view. pub active_layers: Vec, /// View-specific overrides. pub overrides: ViewOverrides, } pub enum ViewKind { FullScore, Part(PartDefinitionId), CondensedScore(CondensationSpec), LeadSheet(LeadSheetSpec), Analysis(AnalysisViewSpec), Custom(CustomViewKindId), } \end{lstlisting} \section{Graph Invariants} \label{sec:graph:invariants} The score graph maintains a set of structural invariants. Implementations \MUST{} preserve these invariants across all edits. \begin{requirement} The following invariants hold over every well-formed score graph: \begin{enumerate} \item Every event in the arena has a valid \texttt{voice} field pointing to a voice that lists the event in its \texttt{events} vector. \item Every event in a voice's events list has its \texttt{voice} field pointing back to that voice. \item Events within a voice are sorted by position and do not overlap in time. \item Every event's \texttt{EventPosition} and \texttt{EventDuration} variants agree with the time model of its enclosing region (Section~\ref{sec:graph:event-time}). For aleatoric regions, agreement is governed by the region's declared \texttt{AleatoricAnchoringDiscipline}. \item Every voice belongs to exactly one \texttt{StaffInstance}; every \texttt{StaffInstance} belongs to exactly one region; every region belongs to the canvas. \item Every \texttt{StaffInstance.staff} resolves to a \texttt{Staff} declared at the score level. A single \texttt{StaffId} \MAY{} be referenced by multiple \texttt{StaffInstance}s in different regions (this is how continuous staff identity is expressed across regions); a \texttt{StaffId} \MUSTNOT{} be referenced by two \texttt{StaffInstance}s in the same region. \item Region time and staff extents do not simultaneously overlap (Section~\ref{sec:graph:canvas}). A region's \texttt{staff\_extent} lists exactly the \texttt{StaffId}s manifested by the region's \texttt{staff\_instances}, with no duplicates. \item Measures belong to exactly one \texttt{StaffInstance}. A region's metric organization is per-staff (admitting polymeter); barline alignment across staves is recorded via \texttt{BarlineAlignmentGroup} entries when explicit alignment is authoritative. \item Every \texttt{TimeAnchor}'s \texttt{AnchorOffset} variant agrees with the time model of the target object's enclosing region (Section~\ref{sec:time:anchors}). \item Every cross-cutting structure's references resolve to extant objects in the graph, except where explicit re-anchoring rules permit transient dangling states during edits (see Chapter~\ref{ch:semops}). \item Every identifier in the graph is unique within its kind. Identifiers reserved for system-derived objects (notably \texttt{VoiceId}s of system-promoted voices) \MUST{} be derived from the deterministic function specified for their kind and \MUSTNOT{} collide with user-authored identifiers. \item Every \texttt{IdentifiedPitch} in the arena has a \texttt{PitchId} that is unique within the score's pitch-identity index. \item Every \texttt{SpellingScope::Pitch(PitchId)} resolves to a live or tombstoned \texttt{PitchId} in the pitch-identity index. A live target resolves to exactly one \texttt{IdentifiedPitch}; a tombstoned target is preserved for operation-log replay, undo, conflict reporting, and historical annotation, and \MUSTNOT{} be silently re-matched to a different live pitch. \item Every notational decomposition attachment's target \texttt{EventId} resolves to a live or tombstoned event; attachments to tombstoned events follow the same preservation discipline as for tombstoned pitches. \item For live decomposition attachments, the targeted event's duration matches the decomposition's component sum. \item Every tuplet's member event durations sum to the tuplet's structurally-required total (Section~\ref{sec:time:tuplets}). Operations that would break this invariant \MUST{} include compensating changes (Section~\ref{sec:semops:deleteevent}). \item Every \texttt{Tie}'s \texttt{pitch\_pairing} entries reference \texttt{PitchId}s that resolve to identified pitches belonging to the start and end events respectively. The class-specific adjacency, voice-membership, and pitch-equivalence rules of Section~\ref{sec:graph:ties} hold for the tie's declared \texttt{TieClass}. \item Every \texttt{Voice} declares an \texttt{origin} consistent with how it was created: \texttt{UserDeclared} for explicit user actions, \texttt{Imported} for foreign-format imports, \texttt{SystemPromoted} for voices allocated by concurrent-edit conflict resolution. The \texttt{VoiceId} of a \texttt{SystemPromoted} voice \MUST{} be the deterministic derivation specified in Section~\ref{sec:graph:promoted-voices}. \item Every \texttt{BarlineAlignmentGroup}'s members reference \texttt{StaffInstance}s within the same region and \texttt{Measure}s belonging to those instances. Group members \MUSTNOT{} reference staff instances or measures across region boundaries. \end{enumerate} Implementations \MUST{} reject graph configurations that violate any of the above invariants. Edits proposed in Chapter~\ref{ch:semops} must either preserve invariants directly or include compensating changes that restore them within the same operation. \end{requirement} This enumeration contains exactly \textbf{19} invariants. (Earlier summary material, including the QUICKSTART, referred to ``18 graph invariants''; the authoritative count is 19, matching this enumeration and the reference implementation.) These 19 are \emph{runtime} invariants: they hold over every well-formed graph and are restored by compensating changes when an edit would break them. Distinct from them are three \emph{construction-time} rejections defined in Chapter~\ref{ch:time}, which a conforming constructor \MUST{} enforce before a value ever enters the graph, and which no runtime invariant restores because the offending value is never a representable graph state: \begin{enumerate} \item A \texttt{TimeSignature}'s beat groups \MUST{} sum to the measure duration (Section~\ref{sec:time:meter}). \item An \texttt{EventOrderingDAG} \MUST{} be acyclic (Requirement~\ref{req:time:ordering-dag-acyclic}). \item A \texttt{TupletRatio} \MUSTNOT{} be degenerate (Section~\ref{sec:time:tuplets}, Requirement~\ref{req:time:tuplet-ratio-construction}). \end{enumerate} \section{Indexes and Auxiliary Structures} \label{sec:graph:indexes} Performance-critical operations require indexed access. The specification states the required indexes; implementations \MAY{} construct additional indexes provided they are kept consistent. \begin{requirement} Implementations \MUST{} maintain at least the following indexes: \begin{description} \item[Event time index.] Maps (region, voice) to a sorted view of event positions, supporting $O(\log n)$ time-range queries. \item[Cross-cutting reference index.] Maps each object identifier to the cross-cutting structures referencing it, supporting $O(1)$ amortized lookup for re-anchoring. \item[Measure index.] Maps measure number (and measure id) to \texttt{MeasureId} for fast navigation. \item[Spelling attachment index.] Maps each \texttt{PitchId} to its attachments, partitioned by analysis layer. \end{description} Indexes \MUST{} be invalidated and rebuilt (or incrementally updated) on the corresponding edits. The semantic operations chapter specifies which operations invalidate which indexes. \end{requirement} \section{Forward References} \begin{itemize} \item The complete catalog of semantic operations, their preconditions, postconditions, and re-anchoring rules is in Chapter~\ref{ch:semops}. \item The mapping from score graph to layout intermediate representation is defined in Chapter~\ref{ch:layout-ir}. \item The on-disk serialization of every type introduced in this chapter is defined in Chapter~\ref{ch:format}. \item Some engraving-specific types (\texttt{StemConfiguration}, \texttt{ArticulationMark}, and similar) are introduced informally here and fully defined in Chapter~\ref{ch:layout-ir} where their interaction with the engraver is specified. The clef/key content model (\texttt{Clef}, \texttt{ClefShape}, \texttt{KeySignature}, \texttt{ClefChange}, \texttt{KeySignatureChange}) and \texttt{LineStyle} are defined normatively \emph{in this chapter} as of schema major~2; the former \texttt{ClefId} identifier sketch is retired in favour of embedded \texttt{Clef} values. \end{itemize} % =========================================================================== \chapter{Semantic Operations and Concurrent Reduction} \label{ch:semops} This chapter specifies the semantic operations on the score graph: the mutations through which the data model becomes a live model. It defines the operation framework, the canonical reduction procedure by which a set of operations becomes a materialized score state, the object existence model with tombstones, the conflict-record machinery, the re-anchoring rules, transactions, and undo. The chapter ends with representative operation specifications. The collaboration mechanics specified here are the foundation on which Chapter~\ref{ch:format}'s synchronization protocols build. The materialized score is the deterministic reduction of an operation set; the file format stores that operation set together with optional acceleration snapshots. \section{Design Principles} \label{sec:semops:principles} \begin{description} \item[The operation set is canonical.] A score's state is defined by the set of operations that have been committed to it. Any materialized graph is a deterministic reduction of that set; caches, snapshots, and partial reductions are acceleration structures, never the source of truth. \item[The replicated operation set is a CRDT; the materialized graph is not.] The grow-only set of operation envelopes with compact causal metadata is a true CRDT: replicas independently accumulate envelopes and converge on the same set. The materialized score graph is not itself claimed to be a CRDT; it is the deterministic reduction of that set, defined by the reduction algorithm specified here. \item[Pairwise operation merge is rejected.] Musical operations do not, in general, commute in intention: transpose-then-respell differs musically from respell-then-transpose; delete-then-tie differs from tie-then-delete. The framework therefore does not attempt to make every pair of operations algebraically commute. Instead, operations are reduced in a canonical order, and that order determines the outcome. \item[Conflicts, no-ops, and rejections are replicated facts.] No operation silently disappears because it could not apply. Every operation has a deterministic \texttt{OperationEffect} recorded as part of the materialized state; every replica reducing the same operation set records the same effects with the same reasons. \item[Identifier-based targeting.] Operations target objects by identifier, never by structural path. Identifiers are stable under concurrent edits; paths are not. \item[Tombstones preserve identity.] Deleted objects retain their identifiers as tombstones. References to tombstoned identifiers are not silently invalidated; they enter a tombstoned-target state unless the operation kind specifies deterministic repair. \item[Transactions materialize atomically.] Primitive members of a transaction \MUSTNOT{} be independently visible as committed score states. Either the whole transaction reduces successfully, or the transaction transitions to a conflict effect with a recorded conflict. \item[Undo is forward, not backward.] Undo is a new operation that computes a compensating edit against the current materialized state; it is not literal time travel. History remains append-only. \item[Two validation modes for advisory checking.] Operations are subject to strict precondition checking in \emph{authoring mode} (interactive edits) and lenient checking in \emph{replay mode} (loading historical operations or applying remote operations). Validation modes apply only to advisory preconditions; invariant preconditions are enforced unconditionally. \end{description} \section{The Operation Framework} \label{sec:semops:framework} \subsection{Operation Identity and Stamps} Operations carry two related but distinct concepts: \emph{identity} (who authored the operation, with what counter) and \emph{stamp} (when the operation was committed, for ordering purposes). \begin{lstlisting}[language=Rust] /// Stable identity of an operation. Determined at authoring time. pub struct OperationId { pub replica: ReplicaId, pub counter: u64, } /// Ordering metadata for an operation. Used to derive the canonical /// reduction order; not part of the operation's identity. pub struct OperationStamp { pub hlc: HybridLogicalClock, pub id: OperationId, } /// A hybrid logical clock combines wall-clock and logical components. pub struct HybridLogicalClock { /// Physical time component, in canonical units (see Chapter 8). pub physical_time: WallClockTime, /// Logical counter, advanced when physical time does not. pub logical_counter: u32, } \end{lstlisting} \begin{requirement} \texttt{OperationId} \MUST{} be stable: an operation's identity is fixed at the moment of authoring and \MUSTNOT{} change with reordering, retransmission, or merging into operation sets at other replicas. \texttt{OperationStamp} \MUST{} be set by the authoring replica using a hybrid logical clock that respects causal order: for any operation $A$ that is in operation $B$'s causal predecessor set, $A$'s stamp \MUST{} be strictly less than $B$'s under the canonical comparison (Section~\ref{sec:semops:reduction-order}). \end{requirement} \subsection{Causal Context via Dotted Version Vectors} Operations carry a compact causal context, not an exhaustive predecessor list. This is essential: exhaustive predecessor lists scale linearly with history and become untenable. \begin{lstlisting}[language=Rust] /// A compact causal context: for each replica known to the authoring /// replica, the highest contiguous counter observed, plus any /// individual operation identifiers known but not contiguous (the /// "dots"). pub struct CausalContext { /// Contiguous counter highest known per replica. pub vector: BTreeMap, /// Individual operations known but not yet contiguous in the /// vector. Used when an authoring replica has observed an /// operation whose causal predecessors include operations not /// yet present. pub dots: BTreeSet, } \end{lstlisting} \begin{requirement} Causal contexts \MUST{} be expressed as dotted version vectors as defined above. Exhaustive predecessor lists \MUSTNOT{} be used as the canonical causal representation; they may appear in debugging output or in the operation log's expanded form, but the wire and storage representation of causal context is the DVV. The per-replica counter floor is \textbf{zero-based} and normative: \texttt{vector[r] == n} asserts that \emph{every} operation \texttt{(r,\,0..=n)} is a causal predecessor --- the contiguous range begins at counter \texttt{0}, not \texttt{1}. A conforming implementation \MUSTNOT{} adopt a one-based floor: doing so would shift the contiguous/dot boundary and make two implementations disagree about which operations are pending versus ready. The membership check (``is operation \texttt{(r,\,c)} covered?'') is \texttt{c <= vector[r]} or \texttt{(r,\,c) in dots}; an implementation \MAY{} evaluate it by walking the known operation ids rather than materializing the full \texttt{0..=n} range, so a sparse high-counter context does not force work proportional to the counter value. Interval tree clocks and other compact causal representations \MAY{} appear in future revisions as performance optimizations but are non-normative in this specification. \end{requirement} \begin{rationale} Dotted version vectors are well-understood, well-tested, and the right complexity tradeoff for the human-scale collaboration model Epiphany targets. Their normative use here closes the high-priority review item about exhaustive predecessor lists scaling badly with history. \end{rationale} \subsection{Operation Envelopes} An operation is transmitted, stored, and reduced as an \emph{envelope}: the operation's payload together with its identity, ordering stamp, causal context, and optional transaction grouping. \begin{lstlisting}[language=Rust] pub struct OperationEnvelope { /// Stable identifier of this operation. pub id: OperationId, /// Author of this operation (may differ from replica in shared /// authoring sessions where multiple authors share a replica). pub author: AuthorId, /// Ordering stamp. Used for canonical reduction order; never /// for identity. pub stamp: OperationStamp, /// Compact causal context: what operations were visible to the /// authoring replica when this operation was created. pub causal_context: CausalContext, /// Optional transaction grouping. Multiple operations sharing a /// TransactionId are reduced atomically. pub transaction: Option, /// The mutation itself. pub payload: OperationPayload, } pub enum OperationPayload { Primitive(OperationKind), /// A meta-operation that explicitly resolves a previously-recorded /// conflict (see Section~\ref{sec:semops:conflicts}). ResolveConflict(ResolveConflictPayload), /// A meta-operation that compensates for a previously-committed /// transaction; the realization of "undo" (see /// Section~\ref{sec:semops:undo}). UndoTransaction(UndoTransactionPayload), /// A meta-operation that resolves an equivocated operation slot /// by naming the chosen candidate envelope (see /// Section~\ref{sec:semops:equivocation}; payload schema in the /// Operation Catalog companion). ResolveEquivocation(ResolveEquivocationPayload), } /// The catalog of primitive operation kinds. The variants listed /// here are normative for the core; additional kinds are introduced /// by registered extensions via the Registered variant. The full /// catalog including detailed payload schemas is the Operation /// Catalog companion specification /// (Appendix~\ref{app:deferred}). pub enum OperationKind { // Event operations InsertEvent(InsertEventOp), DeleteEvent(DeleteEventOp), ModifyEvent(ModifyEventOp), // Pitch and tuning operations RespellPitch(RespellPitchOp), Transpose(TransposeOp), InsertIdentifiedPitch(InsertIdentifiedPitchOp), DeleteIdentifiedPitch(DeleteIdentifiedPitchOp), ModifyIdentifiedPitch(ModifyIdentifiedPitchOp), // Cross-cutting operations CreateCrossCutting(CreateCrossCuttingPayload), DeleteCrossCutting(DeleteCrossCuttingPayload), ModifyCrossCutting(ModifyCrossCuttingPayload), // Repeat-structure operations CreateRepeatStructure(CreateRepeatStructureOp), DeleteRepeatStructure(DeleteRepeatStructureOp), // Region and structure operations ChangeRegionTimeModel(ChangeRegionTimeModelOp), InsertRegion(InsertRegionOp), DeleteRegion(DeleteRegionOp), InsertStaffInstance(InsertStaffInstanceOp), DeleteStaffInstance(DeleteStaffInstanceOp), InsertStaff(InsertStaffOp), CreateVoice(CreateVoiceOp), DeleteVoice(DeleteVoiceOp), // Metric-model operations SetTimeSignature(SetTimeSignatureOp), SetTempoSegment(SetTempoSegmentOp), SetMetricGrid(SetMetricGridOp), // Score settings SetMetadata(SetMetadataOp), // Layout-semantic operations SetUserSystemBreak(SetUserSystemBreakOp), SetUserPageBreak(SetUserPageBreakOp), SetStaffLayout(SetStaffLayoutOp), // Transaction declaration. Carries metadata; member primitives // reference the transaction by id in their envelopes. DeclareTransaction(TransactionDescriptor), // Extension-defined primitive operation. Registered(OperationKindRegistryId, SerializedRegisteredOp), } \end{lstlisting} \subsection{The Operation Set as CRDT} \begin{requirement} The replicated operation set is a grow-only CRDT: replicas accumulate envelopes by gossip, broadcast, or any other delivery mechanism, and converge on the same set when they have observed the same envelopes. The set is set-valued, not multiset-valued: an envelope is a member of the set or it is not, with no count. A replica \MUSTNOT{} discard envelopes from the operation set except by an explicit pruning operation (Chapter~\ref{ch:format}), which preserves canonical state through retained base snapshots and the post-pruning operation set. \end{requirement} \subsection{OperationId Collisions and Replica Equivocation} \label{sec:semops:equivocation} Two received envelopes may carry the same \texttt{OperationId}. Acceptance must not depend on arrival order: if it did, two replicas observing the same envelopes in different orders could diverge on which one is canonical. The operation set therefore maps each \texttt{OperationId} to an \texttt{OperationSlot} rather than directly to an envelope. \begin{lstlisting}[language=Rust] pub enum OperationSlot { /// Exactly one canonical envelope is known for this /// OperationId. The envelope reduces normally. Single(OperationEnvelope), /// Two or more distinct canonical envelopes have been /// observed for this OperationId. The slot is equivocated. /// The operation produces no canonical effect until external /// recovery resolves the equivocation. Equivocated { operation_id: OperationId, /// Every distinct canonical envelope observed for this /// OperationId. Identified by content hash to avoid /// duplicating large payloads. Sorted lexicographically by /// hash for deterministic enumeration. candidates: BTreeSet, }, } /// BLAKE3-256 hash of a canonical envelope serialization with /// domain tag "MUSCENVH". pub struct EnvelopeHash(pub [u8; 32]); \end{lstlisting} \begin{requirement} Slot transitions are determined as follows, independently of arrival order: \begin{itemize} \item If no slot exists for the incoming envelope's \texttt{OperationId}: the incoming envelope is well-formedness checked (Section~\ref{sec:semops:acceptance}) and, if well-formed, a \texttt{Single} slot is created. \item If a \texttt{Single} slot exists and the incoming envelope is byte-identical (canonical serialization) to the slot's envelope: the duplicate is dropped silently; the slot is unchanged. \item If a \texttt{Single} slot exists and the incoming envelope's canonical bytes differ from the slot's envelope: the slot transitions to \texttt{Equivocated} with both envelopes' hashes in its \texttt{candidates} set. The previously-canonical envelope is retained in a diagnostic candidate store keyed by its hash; the incoming envelope is likewise retained. Neither envelope contributes to canonical reduction. \item If an \texttt{Equivocated} slot exists: the incoming envelope's hash is added to its \texttt{candidates} set if not already present; otherwise the duplicate is dropped. The envelope is retained in the candidate store. The slot remains \texttt{Equivocated}. \end{itemize} An equivocated slot \MUSTNOT{} contribute to canonical reduction. Reduction proceeds as if the operation does not exist: any operation that causally depends on the equivocated \texttt{OperationId} is reduced under the same rule as a missing causal predecessor (the dependent operation is retained but produces no effect until the equivocation is resolved). Resolution is by explicit recovery action, not by arrival order or content. Three resolution mechanisms are permitted: \begin{itemize} \item \emph{Transport-level credential revocation}: the collaboration transport revokes the equivocating replica's credentials and the user accepts one envelope timeline as canonical via an explicit reconciliation operation. \item \emph{Local user choice}: the user (or an authorized administrator) issues an explicit \texttt{ResolveEquivocation} operation naming the \texttt{OperationId} and the chosen \texttt{EnvelopeHash}, promoting the slot back to \texttt{Single}. \item \emph{Profile-declared deterministic policy}: a conformance profile \MAY{} declare a deterministic selection function (e.g., ``lowest canonical envelope hash wins''). This is a policy choice, not a default. A bundle reduced under a profile with a declared resolution policy is canonical under that profile; the same bundle reduced under a profile without such a policy remains anomalous. \end{itemize} If a bundle on disk contains an equivocated slot without a resolution policy or pending resolution operation, the bundle is anomalous. Readers \MUST{} open it, if at all, in a diagnostic recovery mode that surfaces the equivocation prominently and that prohibits ordinary editing of the affected portion of canonical state. \end{requirement} \begin{rationale} Replica equivocation (the same replica producing two distinct envelopes with the same \texttt{OperationId}) is a real failure mode: replica-key compromise, software bug, replay attack, or filesystem corruption can all produce it. The \texttt{OperationSlot} model makes equivocation a representable, order-independent state: two replicas that have observed the same set of envelopes converge on the same slot states regardless of the order of observation. Treating the first arrival as canonical would let a malicious early-arriver pin canonical state at one replica while a late-arriver pins different state at another replica; the slot model avoids that asymmetry by refusing to elevate either candidate without an explicit resolution action. Recovery is a policy choice rather than a built-in default because the right answer depends on context: a collaborating team with credential revocation will resolve differently from a single-user backup-restore scenario, which differs again from an adversarial-document forensic scenario. \end{rationale} \subsection{Envelope Acceptance and Malformed-Envelope Behavior} \label{sec:semops:acceptance} Operation envelopes arrive from local authoring and from remote replicas. Acceptance rules determine when an envelope enters the operation set. \begin{requirement} An incoming envelope is well-formed if and only if all of the following hold: \begin{itemize} \item Its \texttt{OperationId} consists of a 64-bit \texttt{ReplicaId} that is not \texttt{ReplicaId::SYSTEM\_DERIVED} (operations are not authored by the system namespace) and a 64-bit counter. \item Its \texttt{stamp.id} is byte-identical to its top-level \texttt{id} field: an envelope's stamp \MUST{} address the same operation as the envelope itself. This eliminates an ambiguity where an authoring replica could otherwise stamp an envelope as belonging to a different operation than its identity declares. \item Its \texttt{stamp.hlc.physical\_time} is a finite, non-negative integer value, expressed in the canonical time unit (Chapter~\ref{ch:format}). \item Its causal context's DVV references only well-formed replica identifiers and non-negative integer counter values; dots are well-formed \texttt{OperationId}s. \item Its payload deserializes successfully against the profile's declared operation catalog. \end{itemize} An envelope that is not well-formed \MUST{} be rejected at reception. Rejection is recorded in the local diagnostic log but does \emph{not} enter the canonical operation set. Remote replicas \MAY{} retransmit; persistent rejection indicates a non-conforming peer. Acceptance \MUSTNOT{} require trust in the envelope's \texttt{stamp.hlc} content: a malicious or misconfigured peer may emit envelopes with implausible future physical times or zero/extreme logical counters. The canonical reduction order (Section~\ref{sec:semops:reduction-order}) consumes the stamp as ordering metadata; it does not validate plausibility. Implementations \MAY{} surface clock-skew warnings in diagnostics but \MUSTNOT{} reorder envelopes for plausibility reasons that would deviate from the canonical reduction order. \end{requirement} \subsection{Per-Replica HLC Monotonicity} \label{sec:semops:hlc-monotonicity} Per-replica monotonicity is a property of the \emph{set} of accepted envelopes, not of arrival order. Out-of-order delivery is normal in gossip systems and is not a violation. \begin{lstlisting}[language=Rust] /// A range of envelopes from a single replica that have been /// identified as monotonicity-violating and excluded from /// ordinary canonical reduction. Retained for diagnostic and /// recovery purposes. pub struct AnomalousReplicaSegment { pub replica: ReplicaId, /// The smallest counter at or after which the replica's /// stream is anomalous. All envelopes from this replica /// with counter >= first_bad_counter are excluded from /// canonical reduction. pub first_bad_counter: u64, /// The detected anomaly reason. pub reason: ReplicaAnomalyReason, /// Operation ids in the excluded segment, retained for /// diagnostic recovery. Sorted by counter. pub excluded: Vec, } pub enum ReplicaAnomalyReason { /// The replica produced an envelope at counter c2 with a /// stamp tuple strictly less than a previously-known /// envelope at counter c1 < c2. HlcMonotonicityViolation { violating_pair: (OperationId, OperationId), }, /// A registered anomaly reason defined by an extension or /// transport. Registered(ReplicaAnomalyRegistryId), } \end{lstlisting} \begin{requirement} For any two envelopes accepted into the operation set that share the same authoring \texttt{ReplicaId}, with operation- counter values $c_1 < c_2$: \begin{quote} The stamp tuple \texttt{(stamp.hlc.physical\_time, stamp.hlc.logical\_counter, id.counter)} of the envelope with counter $c_1$ \MUST{} be lexicographically less than or equal to the corresponding tuple of the envelope with counter $c_2$. \end{quote} Arrival order \MUSTNOT{} be used to determine whether this property holds. If a replica receives counter 10 before counter 9, that is a property of delivery, not of monotonicity. If two envelopes from the same replica violate this property (their stamp tuples are inconsistent with their counter order), the document carries a \textbf{replica equivocation anomaly}. An \texttt{AnomalousReplicaSegment} is created (or extended) for the offending replica. The segment's \texttt{first\_bad\_counter} is the smaller of the two counters in the violating pair. Effects on canonical reduction: \begin{itemize} \item Every envelope from the offending replica with counter $\geq$ \texttt{first\_bad\_counter} is excluded from canonical reduction. Such envelopes are retained on disk and in memory (the CRDT property does not allow silent removal) but are held in the \texttt{AnomalousReplicaSegment}'s \texttt{excluded} list, not in the canonical operation set. \item Envelopes from the offending replica with counter $<$ \texttt{first\_bad\_counter} remain in the canonical operation set and reduce normally. \item Any operation in the canonical set that causally depends on an excluded envelope reduces under the missing-causal-predecessor rule: the dependent operation is retained, produces no canonical effect, and is held pending resolution. \item The document is marked as containing a replica- equivocation anomaly. Ordinary editing \MAY{} continue on the unaffected portion of canonical state; collaborative synchronization with the equivocating replica \MUST{} be suspended until external resolution. \end{itemize} Resolution paths mirror the \texttt{OperationSlot} equivocation rules (Section~\ref{sec:semops:equivocation}): transport-level credential revocation followed by an explicit reconciliation operation, an explicit local recovery action, or a profile-declared deterministic policy. Authentication of replica identities and revocation of compromised replicas are the responsibility of the collaboration transport (Appendix~\ref{app:deferred}). This chapter specifies only local detection, segment formation, exclusion from canonical reduction, and the structural shape of resolution. \end{requirement} \begin{rationale} Excluding the violating segment from canonical reduction rather than letting it reduce-but-mark-tainted is the safer choice: a tainted reduction still produces canonical-looking state derived from operations the spec has flagged as untrustworthy. Excluding the segment keeps canonical state derived only from operations that satisfy the monotonicity invariant, at the cost of holding pending operations that causally depended on the excluded segment. \end{rationale} \section{The Canonical Reduction} \label{sec:semops:reduction} The materialized score graph is the deterministic reduction of the operation set. This section specifies how that reduction is performed. \subsection{Operation Lifecycle} \label{sec:semops:lifecycle} Every operation traverses four phases: \begin{description} \item[Prepare.] The local UI command validates enough to construct a well-formed operation envelope. \emph{Prepare is advisory validation only}: failure here means the UI rejects the user's action and never authors the operation. Prepare does not bind semantics; an operation that successfully prepares at one replica \MAY{} be reduced to a conflict effect at another. \item[Commit.] The operation envelope is added to the replicated operation set. After commit, the operation is part of canonical state and \MUSTNOT{} be discarded outside the pruning protocol. \item[Reduce.] During materialization, the operation is processed by the canonical reduction algorithm against the current materialized state. Reduction is deterministic: every replica with the same operation set produces the same materialized state. \item[Report.] The reduction produces a deterministic \texttt{OperationEffect} for each operation. Effects are visible graph facts: users can inspect what each operation did, and subsequent operations can address conflicts and no-ops directly. \end{description} \subsection{The Reduction Algorithm} \begin{requirement} The materialized score state is computed from the operation set as follows: \begin{enumerate} \item Sort the operation set into the canonical reduction order (Section~\ref{sec:semops:reduction-order}). \item Group consecutive operations that share a transaction id into transaction blocks. \item For each non-transaction operation, apply the operation to the working state, computing its \texttt{OperationEffect}. \item For each transaction block, apply all member operations atomically: either all succeed and the transaction reduces to \texttt{Applied} (with each member's individual effect recorded), or some member fails its invariant preconditions, in which case the whole transaction reduces to a \texttt{Conflicted} effect with a recorded conflict. \item Record every effect in the materialized state's effect log (which is part of canonical state, not implementation detail). \end{enumerate} Given the same operation set, conformant implementations \MUST{} produce score states with byte-identical structural content and byte-identical effect logs. Differences in implementation language, storage layout, threading model, or hardware \MUSTNOT{} produce divergent reductions. \end{requirement} \subsection{Canonical Reduction Order} \label{sec:semops:reduction-order} \begin{requirement} The canonical reduction order is the lexicographic ordering by: \begin{enumerate} \item Causal order: for operations $A$ and $B$, if $A$ is in $B$'s causal context (transitively, via DVV closure), then $A$ precedes $B$. \item For operations not ordered by causal order (i.e., concurrent operations), lexicographic order by: \begin{enumerate} \item \texttt{stamp.hlc.physical\_time} (ascending) \item \texttt{stamp.hlc.logical\_counter} (ascending) \item \texttt{stamp.id.replica} (ascending, lexicographic on the replica identifier's canonical byte form) \item \texttt{stamp.id.counter} (ascending) \end{enumerate} \end{enumerate} Causal order strictly dominates HLC order: wall-clock skew may affect the ordering of concurrent operations but \MUSTNOT{} cause a causal predecessor to sort after a causal successor. The HLC authoring rule (Section~\ref{sec:semops:framework}) guarantees this by construction. \end{requirement} \subsection{Operation Effects} \begin{lstlisting}[language=Rust] /// The deterministic effect of an operation under canonical /// reduction. Recorded as part of materialized state. pub enum OperationEffect { /// The operation applied cleanly with no compensating changes. Applied, /// The operation applied with deterministic compensating changes /// (e.g., re-anchoring records, attachment migration). AppliedWithRepair { repairs: Vec }, /// The operation could not apply cleanly; a conflict record was /// created in the conflict registry. Conflicted { conflict: ConflictId }, /// The operation's target was tombstoned. The operation is /// preserved in the operation set but has no effect on the /// materialized graph beyond the recorded effect. TombstonedTarget { target: TypedObjectId }, /// The operation reduces to no effect. The reason is recorded /// and is part of canonical state. NoOp { reason: NoOpReason }, } pub enum NoOpReason { /// The operation's target was tombstoned by a causally-prior /// operation, and the target operation has no compensating /// repair rule. TargetTombstoned, /// The operation duplicates a causally-prior operation's effect. AlreadyApplied, /// A later operation in the canonical order subsumed this /// operation's effect (e.g., a later DeleteEvent on the same /// target supersedes an earlier RespellPitch). SupersededByLaterOperation { superseder: OperationId }, /// An invariant precondition that was satisfied at authoring /// time fails when the operation is reduced under concurrent /// edits. Distinguished from Conflicted because the operation's /// intent is not preserved: it simply has no applicable effect. /// The failure reason is a typed enum, not a free-form string: /// canonical effects MUST NOT contain free-form text, since two /// implementations could otherwise produce divergent canonical /// state while agreeing semantically. PreconditionFailedUnderReduction { reason: PreconditionFailureReason, }, /// The operation belonged to a transaction whose other members /// failed; the transaction was conflicted, and this operation /// produced no individual effect. TransactionConflict, } pub enum PreconditionFailureReason { /// The target object identifier did not exist in the working /// state at the moment of reduction. TargetMissing, /// The target object was tombstoned by a causally-prior /// operation. Distinguished from NoOpReason::TargetTombstoned /// in that the kind's rules permitted an attempt at /// repair-on-tombstone, but the repair itself failed precondition. TargetTombstoned, /// The operation requires a region time model (e.g., metric) /// different from the one currently in effect at the target /// position. WrongRegionTimeModel, /// A tuplet compensation declared by the operation is invalid /// against the current tuplet structure at the target. TupletCompensationInvalid, /// An event duration the operation specifies is invalid in /// the target voice or region (e.g., overlapping a tied /// chord-onset boundary in an incompatible way). EventDurationInvalid, /// The operation's target position falls outside the region /// declared by its envelope, or addresses a region that does /// not exist. PositionOutsideRegion, /// A pitch-space or tuning-context precondition failed: e.g., /// the operation declared a pitch in a pitch space that is not /// active at the target region. PitchSpaceMismatch, /// A voice precondition failed: the operation targeted a /// voice that does not exist or has been tombstoned. VoiceMissing, /// An extension-declared precondition failed. The extension /// is identified; the extension's own catalog enumerates the /// specific precondition codes. ExtensionPrecondition(ExtensionPreconditionId), /// Registered precondition code defined by a versioned /// registry. Registered(PreconditionFailureRegistryId), } /// A deterministic compensating change made during reduction. Each /// RepairRecord describes one repair the reduction performed to /// preserve graph invariants in the presence of a tombstoning or /// migrating operation. Recorded as part of canonical state. pub struct RepairRecord { /// The kind of repair performed. pub kind: RepairKind, /// The object affected by this repair. pub target: TypedObjectId, } pub enum RepairKind { /// A reference was re-anchored to a surviving target per the /// re-anchoring rule table (Section~\ref{sec:semops:reanchor}). Reanchored { from: TypedObjectId, to: TypedObjectId, reason: ReanchorReason, }, /// A spanner or beam had members removed but enough remained /// for the structure to survive. SpannerTruncated { removed_members: Vec, }, /// A user-content reference was orphaned (target lost; reference /// preserved for diagnostic purposes). Orphaned, /// A reference whose existence required its target was /// cascade-deleted. CascadeDeleted, /// An attachment transitioned to tombstoned-target state. AttachmentTombstoned, /// A voice was promoted to a system-derived voice identity due /// to concurrent insertion collision. VoicePromoted { from: VoiceId, to: VoiceId, }, /// A tuplet was compensated according to the operation's /// declared TupletCompensation. TupletCompensated { compensation_kind: TupletCompensationKind, }, /// A registered extension-defined repair kind. Registered(RepairKindRegistryId), } \end{lstlisting} \begin{requirement} Every operation in the operation set \MUST{} produce exactly one \texttt{OperationEffect} under reduction. Effects \MUST{} be deterministic: every replica reducing the same operation set in the canonical order produces the same effect for each operation, including identical \texttt{NoOpReason} values where applicable. \end{requirement} \subsection{Cache and Snapshot Discipline} \label{sec:semops:caches} \begin{requirement} Caches, snapshots, and partial reductions are acceleration structures only. They \MUSTNOT{} affect the canonical materialized state, which is defined solely by the operation set, the active conformance profile, and the normative reduction algorithm. Implementations \MUST{} invalidate or recompute caches whenever any of the following change: \begin{itemize} \item The visible operation set (additions, or pruning operations under the file-format protocol). \item The causal order derived from any envelope's causal context. \item The reduction algorithm version declared by the active conformance profile. \item The active conformance profile itself. \end{itemize} Cache hits \MUST{} return data that is bit-identical to the data a fresh reduction would produce. Caches that cannot guarantee this property \MUST{} be invalidated. \end{requirement} \section{Object Existence and Tombstones} \label{sec:semops:tombstones} \subsection{Add-Tombstone Semantics} Object lifetimes in the materialized graph follow add-tombstone semantics: an operation creates an object by minting a stable identifier, and an operation deletes an object by tombstoning that identifier. Tombstoned identifiers are retained; their resolution yields a deletion record rather than the original object. \begin{requirement} Identifiers \MUST{} have one of three states in the materialized graph: \begin{itemize} \item \textbf{Live.} The identifier resolves to a current object. \item \textbf{Tombstoned.} The identifier resolves to a deletion record carrying the operation that performed the deletion and the operation that originally minted the identifier. \item \textbf{Unknown.} The identifier has not been seen by this replica. This state is observable in collaborative scenarios where envelopes arrive out of order; it resolves to either Live or Tombstoned once causal predecessors arrive. \end{itemize} Implementations \MUSTNOT{} resurrect tombstoned identifiers (recreating an object with a previously-tombstoned id) except through an explicit inverse-of-delete operation in the operation set. Independent re-creation is forbidden. \end{requirement} \subsection{Tombstone Retention} \begin{requirement} Tombstones \MUST{} be retained while any of the following hold: \begin{itemize} \item Pending replication: operations referencing the tombstoned identifier may not yet have been observed by all replicas. \item Pending undo: a transaction containing the deletion is within undo range. \item Active conflict resolution: a conflict record references the tombstoned identifier and has not been resolved. \item Attachment resolution: a tombstoned-target attachment remains in the score for historical or diagnostic purposes. \end{itemize} Tombstone garbage collection is governed by the pruning protocol in Chapter~\ref{ch:format}. Tombstones \MUSTNOT{} be discarded outside that protocol. \end{requirement} \subsection{Reference Resolution Across Tombstones} \begin{requirement} A reference to a tombstoned identifier resolves to a \texttt{TombstonedTarget} state rather than being silently invalidated. Operations targeting tombstoned identifiers reduce to \texttt{TombstonedTarget} or \texttt{NoOp} effects according to their kind's declared rules (Section~\ref{sec:semops:examples}). Re-anchoring rules (Section~\ref{sec:semops:reanchor}) define how cross-cutting structures and attachments respond to tombstoned references at reduction time. \end{requirement} \section{Conflict Records} \label{sec:semops:conflicts} When an operation cannot apply cleanly, the reduction produces a \texttt{Conflicted} effect referencing a \texttt{ConflictRecord} in the canonical conflict registry (a component of materialized state; see below). Conflict records are first-class: stable, addressable, visible to users, and addressable by subsequent operations. \subsection{Conflict Record Type} \begin{lstlisting}[language=Rust] pub struct ConflictRecord { /// Content-derived identifier (Section below). pub id: ConflictId, /// The operations that participated in producing this conflict. /// At least two for a true conflict; one for an operation that /// failed precondition checking under reduction. pub caused_by: Vec, /// The kind of conflict, governing what resolution options /// exist. pub kind: ConflictKind, /// Objects affected by this conflict, for diagnostic and UI /// navigation purposes. pub affected_objects: Vec, /// Current resolution state. Conflicts begin Unresolved; a /// later ResolveConflict operation transitions them to Resolved /// or Dismissed. pub resolution_state: ConflictResolutionState, } pub enum ConflictKind { /// Two concurrent operations attempted to write the same /// non-LWW field. The winning operation's effect is in the /// materialized state; the losing operation's effect is in the /// conflict record for user inspection. StructuralFieldCollision { winner: OperationId, loser: OperationId, field: FieldPath, }, /// A transaction's primitive members were partially applicable /// under reduction; the whole transaction was rejected. TransactionConflict { transaction: TransactionId, failed_members: Vec, }, /// An operation targeted a tombstoned object and could not be /// repaired by the kind's declared rules. TombstonedTarget { target: TypedObjectId, operation: OperationId, }, /// Re-anchoring failed: no deterministic target could be found. ReanchorFailure { original_referent: TypedObjectId, referencing_object: TypedObjectId, }, /// A change-region-time-model operation produced contained /// events whose coordinate kinds are incompatible with the new /// time model; the migration could not be performed /// deterministically. TimeModelMigrationFailure { region: RegionId, incompatible_events: Vec, }, /// A registered extension operation's reduction failed in a /// way described by the extension. Opaque to the core. ExtensionConflict { kind_id: ConflictKindRegistryId, details: SerializedExtensionConflict, }, } pub enum ConflictResolutionState { Unresolved, Resolved { by: OperationId, action: ResolutionAction }, Dismissed { by: OperationId }, } // See Requirement (field-collision effect tag): for a // StructuralFieldCollision the winner (later op) reads Conflicted; // the earlier op retains Applied. pub enum ResolutionAction { /// Accept the losing operation's effect (replacing the winner). AcceptLoser, /// Reapply the winner explicitly (no semantic change; clears the /// conflict). KeepWinner, /// User-authored replacement that supersedes both. Override { override_operation: OperationId }, /// Re-anchor to a user-chosen target. Reanchor { new_target: TypedObjectId }, /// Dismiss the conflict without changing the materialized graph: /// the user acknowledges it and accepts the current (winner) /// state. Selects the Dismissed resolution state. Dismiss, /// Custom resolution for registered conflict kinds. Registered(ResolutionRegistryId), } \end{lstlisting} \begin{requirement} \label{req:semops:resolution-action-discriminants} The discriminant byte of \texttt{ResolutionAction} is its declaration-order index in the listing above: \texttt{AcceptLoser} $= 0$, \texttt{KeepWinner} $= 1$, \texttt{Override} $= 2$, \texttt{Reanchor} $= 3$, \texttt{Dismiss} $= 4$, \texttt{Registered} $= 5$. A \texttt{ResolutionAction} is encoded into the \texttt{ResolveConflict} operation payload and therefore into that operation's content hash, so this assignment is normative and stable and \MUSTNOT{} be reordered. \texttt{Dismiss} was assigned \texttt{4} ahead of \texttt{Registered} (\texttt{5}) when the \texttt{Dismissed} resolution state was made reachable by an authored operation (Section~\ref{sec:semops:conflict-resolution} below), fixing the wire form against a silent shift. \end{requirement} \begin{requirement} \label{req:semops:field-collision-effect} When two concurrent operations write the same non-LWW field and reduction records a \texttt{StructuralFieldCollision}, the per-operation \texttt{OperationEffect} is assigned as follows: the \emph{winner} --- the operation later in canonical order, whose choice materializes and whose processing created the conflict record --- reads \texttt{Conflicted\{conflict\}}; the \emph{earlier} (losing) operation retains the \texttt{Applied} effect it received when it was first reduced. The winner carries the flag because it is the single operation that both materializes the field and observes the collision, which makes the assignment independent of the order in which envelopes arrive at any replica. \end{requirement} \begin{rationale} The alternative --- tagging the loser \texttt{Conflicted} (or \texttt{NoOp\{SupersededByLaterOperation\}}) because its intent did not survive --- is defensible and arguably more intuitive for a UI that surfaces ``your edit was overridden.'' We pin \emph{winner-carries} instead because it is computed at the moment the collision is detected (when the winning operation is reduced against already-present state), needs no second pass to retroactively re-tag an earlier operation, and is manifestly order-independent. A UI that wishes to tell the losing author their edit was overridden reads the \texttt{StructuralFieldCollision} record's \texttt{loser} field rather than the loser operation's effect. \end{rationale} \subsection{The Conflict Registry} Canonical materialized state carries a top-level conflict registry (the score graph does not --- conflict records are a reduction product, not authored content): \begin{lstlisting}[language=Rust] pub struct ConflictRegistry { pub records: Vec, } \end{lstlisting} \begin{requirement} The conflict registry is part of canonical materialized state. Conflict records persist until they are resolved or dismissed by explicit \texttt{ResolveConflict} operations. Conflict records \MUSTNOT{} be silently removed; their lifetime is governed by the operation set, not by UI state. The layout pipeline (Chapter~\ref{ch:layout-ir}) \SHOULD{} provide visual indication of unresolved conflicts at their affected objects; this is the user-facing manifestation of the conflict registry's contents. \end{requirement} \subsection{ConflictId Derivation} \label{sec:semops:conflict-id} Conflict records are produced during deterministic reduction, not authored by replicas. Their identifiers \MUST{} therefore be content-derived, not minted from a local replica-plus-counter sequence. Otherwise, two replicas reducing the same operation set would produce conflict registries with disagreeing identifiers, breaking canonical state. \begin{requirement} Each \texttt{ConflictId} \MUST{} be derived deterministically from the conflict's content via BLAKE3 truncation: \begin{lstlisting}[language=Rust] fn derive_conflict_id( kind: &ConflictKind, causing_operations: &[OperationId], affected_objects: &[TypedObjectId], ) -> ConflictId { let mut preimage = Vec::new(); preimage.extend_from_slice(b"MUSCCONF"); preimage.extend_from_slice(&kind.canonical_bytes()); // Sort causing operations lexicographically by canonical bytes let mut sorted_ops = causing_operations.to_vec(); sorted_ops.sort_by(|a, b| a.canonical_bytes().cmp(&b.canonical_bytes())); for op in &sorted_ops { preimage.extend_from_slice(&op.canonical_bytes()); } // Sort affected objects lexicographically by canonical bytes let mut sorted_objs = affected_objects.to_vec(); sorted_objs.sort_by(|a, b| a.canonical_bytes().cmp(&b.canonical_bytes())); for obj in &sorted_objs { preimage.extend_from_slice(&obj.canonical_bytes()); } let hash = blake3(&preimage); ConflictId(u128::from_be_bytes( hash[0..16].try_into().unwrap() )) } \end{lstlisting} The canonical \texttt{ConflictKind} byte representation \MUST{} include the kind discriminant and all kind payload (winning operation, losing operation, field path, transaction id, tombstoned target, original referent, region, extension kind identifier, etc.), so that distinct conflicts have distinct preimages by construction. Implementations \MUSTNOT{} introduce an ordinal or local counter into the preimage; conflicts that share kind, causing operations, and affected objects are by definition the same conflict. \end{requirement} \begin{rationale} Content-derived identifiers are the only mechanism that makes the conflict registry a deterministic materialized fact. Every replica reducing the same operation set produces the same conflict records with the same identifiers, addressable by subsequent \texttt{ResolveConflict} operations whose payloads carry those identifiers by reference. Truncation from 256 to 128 bits is safe: the birthday-collision probability for any practical conflict count is vanishingly small, and the same truncation discipline already governs other 128-bit identifiers in the format. \end{rationale} \subsection{Conflict Resolution Operations} \label{sec:semops:conflict-resolution} \begin{lstlisting}[language=Rust] pub struct ResolveConflictPayload { pub target: ConflictId, pub action: ResolutionAction, } \end{lstlisting} \begin{requirement} A \texttt{ResolveConflict} operation transitions a conflict's state from \texttt{Unresolved} to one of two states selected by its \texttt{action}. The \texttt{ResolutionAction::Dismiss} action transitions the conflict to \texttt{Dismissed} (no semantic change to the materialized graph beyond the conflict record's state: the user acknowledges the conflict and accepts the current winner state). Every other \texttt{action} transitions the conflict to \texttt{Resolved} with that action applied. Both resolution states are thus reachable by an authored operation; \texttt{Dismiss} is the authored selector for \texttt{Dismissed}, closing the prior gap in which \texttt{Dismissed} was a representable state with no operation to reach it. Concurrent \texttt{ResolveConflict} operations on the same conflict are themselves subject to canonical reduction: the earlier operation under the canonical order is applied; the later operation reduces to \texttt{NoOp} with reason \texttt{AlreadyApplied} if its action is equivalent, or to \texttt{Conflicted} with a meta-conflict record if its action differs. \end{requirement} \section{Transactions} \label{sec:semops:transactions} Transactions are first-class replicated operation groups. They reduce atomically: either every primitive member applies, or the whole transaction conflicts. \begin{lstlisting}[language=Rust] pub struct TransactionDescriptor { pub id: TransactionId, /// Human-readable label, for the undo history and diagnostics. pub label: String, /// Optional categorization (used by UIs and analytics). pub category: Option, } pub struct TransactionId(pub u128); /// Open vocabulary: a minimal core set plus a Registered escape for /// extension-defined categories. pub enum TransactionCategory { NoteEntry, Structural, Layout, Import, Registered(OperationKindRegistryId), } \end{lstlisting} \begin{requirement} \label{req:semops:transaction-category} \texttt{TransactionCategory} is an \emph{open} vocabulary. Its normative core set is exactly \texttt{NoteEntry}, \texttt{Structural}, \texttt{Layout}, and \texttt{Import}; any other category \MUST{} be expressed as \texttt{Registered(OperationKindRegistryId)} rather than by extending the core set. The field is advisory (consumed by UIs and analytics, never by canonical reduction), so the minimal core set is the right tradeoff: under-specifying is cheap to extend through \texttt{Registered}, whereas baking speculative categories into the core vocabulary is not. The discriminants follow declaration order (\texttt{NoteEntry} $= 0$ through \texttt{Registered} $= 4$). \end{requirement} Each primitive operation belonging to a transaction carries the transaction's identifier in its envelope's \texttt{transaction} field. The transaction descriptor itself is a separate operation envelope (\texttt{DeclareTransaction}) whose payload contains the \texttt{TransactionDescriptor}. \subsection{Transaction Descriptor Causal Ordering} \label{sec:semops:transaction-causal} The canonical reduction order is causal-first, then HLC, then operation id (Section~\ref{sec:semops:reduction-order}). For the descriptor to be available when its members are reduced, the ordering between descriptor and members must be guaranteed by causal dependency, not by HLC stamps. \begin{requirement} Every primitive operation member of a transaction \MUST{} causally depend on the transaction's \texttt{DeclareTransaction} envelope: the descriptor's \texttt{OperationId} \MUST{} appear in the member's \texttt{causal\_context} (either in the per-replica DVV range or among its dots). Authoring implementations \MUST{} observe and include the descriptor in the causal context of each member they author. Delivery order need not respect this dependency: an out-of-order delivery in which a member arrives before its descriptor is normal and is handled by the canonical reduction algorithm, which sorts by causal order before reducing. At reduction time: \begin{itemize} \item If the descriptor is present in the operation set and causally precedes every member: the transaction is well-formed; it is reduced atomically per the rule below. \item If a primitive operation declares membership in a transaction whose \texttt{DeclareTransaction} envelope is absent from the operation set, or whose envelope is present but does not causally precede the member: the member is malformed against the transaction model. It reduces to \texttt{NoOp\{reason: TransactionConflict\}} and contributes to a synthesized conflict record of kind \texttt{ConflictKind::TransactionConflict} naming the malformed member and the missing-or-misordered descriptor. The member is preserved in the operation set (the CRDT property prohibits removal) but produces no effect on canonical state beyond the recorded conflict. \end{itemize} \end{requirement} \begin{rationale} Requiring causal dependency rather than HLC ordering matches how transactions actually arise at the authoring replica: a user begins an edit (descriptor), then commits its primitives. The primitives can causally observe the descriptor because they are authored after it on the same replica. The canonical reduction order then handles them correctly without any special transaction-extraction phase. This rule also makes equivocating descriptors impossible: a member cannot claim membership in a transaction whose descriptor it has not seen. \end{rationale} \begin{requirement} Transactions reduce atomically. During reduction: \begin{itemize} \item All primitive members of a transaction are gathered into a transaction block at the point of the earliest member's canonical position. \item The block is reduced as a single unit. Each member is evaluated against the working state produced by prior members within the same block. \item If every member's invariant preconditions are satisfied, the transaction reduces to \texttt{Applied}; each member's individual effect is recorded as part of the transaction's composite effect. \item If any member's invariant preconditions fail, the entire transaction reduces to \texttt{Conflicted}: a conflict record is created with kind \texttt{ConflictKind::TransactionConflict}, listing the failed members; no member's effect is applied; each member's individual effect is recorded as \texttt{NoOp\{reason: TransactionConflict\}}. \end{itemize} Primitive members of a transaction \MUSTNOT{} be observable as independently committed score states. Observers see either the pre-transaction state or the fully applied post-transaction state; they never see a partial intermediate. \end{requirement} \begin{rationale} Atomic transaction reduction is essential for the tuplet compensation model (Section~\ref{sec:semops:deleteevent}): the delete operation and its compensating insert (or rewrite, or cascade) are committed together as a single transaction, so the score is never observably broken between them. Local materialization may stream a transaction's primitives to the UI for responsive feedback, but the materialized canonical state visible to remote observers transitions only at transaction boundaries. \end{rationale} \section{Re-Anchoring} \label{sec:semops:reanchor} When an operation tombstones an object, every other object referencing it must respond deterministically. The response is specified as a total deterministic function. \subsection{The Re-Anchor Function} \begin{lstlisting}[language=Rust] pub enum ReanchorResult { /// The reference is replaced with a reference to a new target. Reanchored { new_target: TypedObjectId, reason: ReanchorReason, }, /// The reference is preserved but its target is tombstoned. /// The referencing object remains in the graph in a /// tombstoned-target state. TombstonedTarget, /// The referencing object is marked orphaned but retained. Orphaned, /// Re-anchoring cannot be performed deterministically; a /// conflict is recorded. Conflicted { conflict: ConflictId }, /// The referencing object is cascade-deleted (tombstoned). CascadeDeleted, } pub enum ReanchorReason { SameVoiceNearer, SameStaffInstanceNearer, SameStaffNearer, SameRegionNearer, ExplicitFallback, DeclaredByExtension(ReanchorReasonRegistryId), /// A rank-4 (same-canvas) proximity survivor. Appended in /// Pass 12 (P12-C4); its wire discriminant is 6 because /// DeclaredByExtension already owned 5 when it was appended. SameCanvasNearer, } \end{lstlisting} A re-anchor that selects a rank-4 (same-canvas) survivor records \texttt{SameCanvasNearer} (ratified Pass~12); before the variant was appended, such repairs were recorded \texttt{ExplicitFallback}, a recording this revision supersedes. \subsection{Total Ordering for "Nearest"} The notion of ``nearest surviving anchor'' is defined as a total ordering. There is no discretionary search and no implementation freedom to choose among ``equally near'' candidates. \begin{requirement} For an object kind $K$ requiring re-anchoring to the nearest surviving $K$, ``nearest'' is computed as the strict lexicographic minimum over the surviving candidates of: \begin{enumerate} \item Containment proximity (smaller is closer): same voice $(0)$, same staff instance $(1)$, same staff $(2)$, same region $(3)$, same canvas $(4)$. Candidates further away than the kind's declared maximum proximity are excluded. \item Absolute time distance from the tombstoned referent's resolved position, measured under a canonical conversion (musical time within metric regions, wall-clock time within proportional regions, declared per-region for aleatoric regions). \item Direction preference: forward $(0)$ before backward $(1)$, unless the kind declares the opposite preference. \item Object identifier (lexicographic on the typed identifier's canonical byte form, ascending) as the final tie-break. \end{enumerate} If no candidate satisfies the kind's declared proximity bound, the result is \texttt{Orphaned} (for user-content kinds such as comments and analytical annotations) or \texttt{CascadeDeleted} (for kinds whose existence requires their target, such as ties and beams). Numeric proximity bounds, direction preferences, and the orphaned-versus-cascade choice are declared per kind in the re-anchoring rule table below; they are part of the kind's conformance contract and \MUSTNOT{} be reinterpreted by implementations. \end{requirement} \subsection{The Re-Anchoring Rule Table} The following table specifies, for each (referencing kind, referent kind) pair, the re-anchoring action under canonical reduction. The table is normative. \begin{longtable}{p{3.0cm} p{2.6cm} p{2.4cm} p{4.4cm}} \toprule \textbf{Referencing} & \textbf{Referent} & \textbf{Action} & \textbf{Parameters} \\ \midrule \endhead Slur & Endpoint event & Re-anchor to the nearest surviving endpoint; cascade-delete only when no endpoint survives & a two-endpoint slur collapses onto its sole survivor (a reference-clean degenerate form); proximity-aware re-targeting is a deferred refinement \\ Slur & Interior event & No action & --- \\ Tie & Either endpoint & Cascade-delete & Tie's existence requires both endpoints. \\ Beam & Member event & Truncate. Cascade-delete if fewer than two members remain & proximity max: same voice \\ Tuplet & Member event & Operation \MUST{} provide tuplet compensation (Section~\ref{sec:semops:deleteevent}); otherwise the operation conflicts at precondition check & --- \\ Spanner & Anchor & Re-anchor to the nearest surviving anchor; cascade-delete only when no anchor survives & as for slurs (surviving-endpoint collapse); per-spanner-kind proximity bounds are a deferred refinement \\ Repeat structure & Anchor & Re-anchor to the nearest surviving anchor; cascade-delete only when no anchor survives & as for spanners; the rule spans \emph{every} event-referencing anchor site of the structure --- \texttt{start}/\texttt{end}, the kind's jump targets (\texttt{DaCapo.end\_target}, \texttt{DalSegno.segno}/\texttt{end\_target}), and each volta's span. Among multiple surviving candidates ``nearest'' is currently the deterministic identifier-order minimum; proximity-aware (four-key) selection is a deferred refinement, as for spanners (ratified with the repeat-authoring pair, schema-major-2 revision) \\ Marker & Anchor & Re-anchor to nearest event in same staff instance & proximity max: same staff instance; orphan on failure \\ Comment & Anchor & Orphan & User content never silently deleted. \\ Cue event & Source event & Cascade-delete on any source deletion & a multi-source cue cascades when \emph{any} source dies (ratified Pass~12): losing any source breaks the cue's quotation integrity; truncate-while-any-source-survives was considered and rejected \\ Graphic gesture & Anchor event & Re-anchor to nearest surviving event of same staff instance; for Free anchoring, no action; for Range anchoring, truncate & proximity max: same staff instance. Truncate (ratified Pass~12): a dead event-anchored range endpoint moves to its containing region's edge --- a start endpoint to the region start, an end endpoint to the region end, zero offset \\ Trajectory event & Endpoint pitch & Re-anchor: replace \texttt{EventPitch(PitchId)} with \texttt{ExplicitPitch} capturing the tombstoned pitch's last known value & --- \\ Analytical annotation & Anchor & Re-anchor to time range preserving original extent; orphan if range cannot be reconstructed & orphaning is the sanctioned outcome (ratified Pass~12) for a wall-clock (region-relative) or indeterminate event span that no stored \texttt{Range} anchor form can express without region-origin resolution \\ Spelling attachment & Target pitch & Transition to tombstoned-target state & Attachment is preserved for diagnostic and undo purposes. \\ Decomposition attachment & Target event & Transition to tombstoned-target state & Attachment preserved for diagnostic and undo purposes. \\ Voice & Containing staff instance & Cascade-delete & --- \\ Event & Containing voice & Cascade-delete & --- \\ Region & Canvas & Refuse (canvas is not deletable) & --- \\ Part definition & Referenced staff & Remove staff from part's staff list; orphan if part becomes empty & --- \\ \bottomrule \end{longtable} \begin{requirement} Re-anchoring \MUST{} be performed as part of the same reduction step that tombstones the referent. The resulting graph state \MUST{} satisfy every invariant in Section~\ref{sec:graph:invariants}. Re-anchoring actions \MUST{} be recorded as \texttt{RepairRecord} entries in the triggering operation's effect. \end{requirement} \section{Undo} \label{sec:semops:undo} Undo is not literal time travel: it is the authoring of a new operation that compensates for a previously-committed transaction against the current materialized state. This preserves the append- only operation set and is correct under concurrent edits. \subsection{UndoTransaction Payload} \begin{lstlisting}[language=Rust] pub struct UndoTransactionPayload { /// The transaction being undone. pub target: TransactionId, /// Policy governing how to undo when the target's effects /// have been partially superseded by later operations. pub policy: UndoPolicy, } pub enum UndoPolicy { /// Compute the inverse semantic operations against the current /// materialized state. If any cannot apply cleanly (because the /// target objects have been tombstoned, fields have been /// further modified, etc.), the entire undo conflicts. StrictInverse, /// Undo what remains valid; record conflicts or no-ops for the /// rest. The undo operation succeeds, but partial. BestEffort, /// Also undo operations that are causally or semantically /// dependent on the target transaction. Dependency is computed /// from causal contexts and explicit dependency links; /// implementations MUST NOT infer broad musical dependence /// heuristically. Cascade, } \end{lstlisting} \subsection{Undo Semantics} \begin{requirement} An \texttt{UndoTransaction} operation is committed to the operation set like any other operation; it does not modify history. Its reduction computes a compensating edit against the materialized state at the point of its canonical position: \begin{itemize} \item For \texttt{StrictInverse}: the inverse of each primitive in the target transaction is computed against the current working state. If every inverse applies cleanly, the undo transaction reduces to \texttt{Applied} with all compensating effects recorded. If any inverse cannot apply (target tombstoned, field modified, etc.), the undo transaction reduces to \texttt{Conflicted} with a conflict record. \item For \texttt{BestEffort}: inverses that apply cleanly are applied; inverses that cannot are recorded as \texttt{NoOp} effects with their reasons. The undo transaction reduces to \texttt{AppliedWithRepair}. \item For \texttt{Cascade}: the dependency set is computed first (causal closure plus explicit dependency links). The transaction's inverses, plus inverses of dependents in reverse order, are applied per the chosen sub-policy (typically \texttt{StrictInverse}). \end{itemize} Equality of the resulting graph state to the pre-target state is defined as \emph{content equivalence}: every live object's fields, every attachment, every cross-cutting structure matches. Operation identifiers, stamps, undo stacks, caches, and storage layout are \emph{not} required to match. \end{requirement} \begin{rationale} Defining undo as a forward operation eliminates the contradiction in earlier passes where undo had to ``restore previous state'' while remote edits had been integrated. The compensating-edit approach respects the operation set's append-only nature and generalizes cleanly to concurrent collaboration: undoing a transaction whose effects have been entwined with remote edits is expressible as a deterministic compensating operation, possibly partial. \end{rationale} \section{LWW Discipline} \label{sec:semops:lww} Last-writer-wins reduction is permitted only for explicitly-marked advisory scalar fields. Structural musical objects are governed by their kind's operation-specific reduction rules. \subsection{The LwwAdvisory Marker} Fields eligible for LWW reduction are explicitly marked as \texttt{LwwAdvisory}. No unmarked field may use LWW semantics. \begin{lstlisting}[language=Rust] /// Marker attribute, applied to fields whose concurrent modifications /// are resolved by last-writer-wins under the canonical reduction /// order. #[attribute_only] pub struct LwwAdvisory; // Example field declarations: // // #[lww_advisory] // pub title: Option // // #[lww_advisory] // pub composer: Option \end{lstlisting} \subsection{Eligible Fields} \begin{requirement} The following classes of field \MAY{} be marked \texttt{LwwAdvisory} and reduced by last-writer-wins: \begin{itemize} \item Score metadata: title, subtitle, composer, lyricist, copyright, work number, opus, dedication, free-form annotations. \item View preferences: which layers are active, which parts are shown, viewport, zoom level (per-replica state in addition; only the explicitly shared portions are LWW). \item Layout hint preferences: default page size, default margins, default staff spacing recommendations. \item Page-break and system-break preferences on layout-semantic structures (the user's stated preference, distinct from the engraver's decision). \item Optional advisory display hints on cross-cutting structures: line color preference, line-style preference, label-position preference. Endpoints, target identities, semantic class, and attachment ownership \MUSTNOT{} be LWW. \end{itemize} All other fields, including every field carrying musical structure, identity, ownership, or semantics, \MUST{} use operation-specific reduction rules defined per operation kind. Implementations \MUSTNOT{} default to LWW for unmarked fields. \end{requirement} \begin{rationale} LWW is acceptable for fields where the worst case of arbitrary deterministic resolution is benign: two authors editing the composer's name concurrently is a metadata collision, not a musical loss. LWW is catastrophic for fields where wholesale replacement destroys musical meaning: two authors editing a chord's pitch list concurrently must not produce ``one author's chord wins.'' The marker discipline makes the boundary explicit and auditable. \end{rationale} \section{Validation Modes} \label{sec:semops:validation} Operations are subject to precondition checking. Two modes distinguish authoring contexts: \begin{description} \item[Authoring mode.] Interactive edits. All preconditions are enforced: invariant preconditions (which preserve graph invariants) and advisory preconditions (which encode user-intent constraints, range checks, and style policy). \item[Replay mode.] Replaying historical operations or applying remote operations under reduction. Only invariant preconditions are enforced; advisory preconditions are skipped, since they represent the authoring replica's local policy at the moment of authoring, not invariants of the canonical state. \end{description} \begin{requirement} Every operation specification \MUST{} classify each precondition as invariant or advisory. Invariant preconditions \MUST{} hold in all modes; advisory preconditions \MUST{} hold in authoring mode and \MAY{} fail silently in replay mode. Implementations \MUSTNOT{} reclassify a precondition without a corresponding revision to this specification. \end{requirement} \section{Per-Kind Reduction Summary} \label{sec:semops:per-kind-summary} The following table summarizes the reduction discipline for each representative operation kind. Detailed specifications appear in the next section. \begin{longtable}{p{3.4cm} p{4.0cm} p{5.0cm}} \toprule \textbf{Operation} & \textbf{Reduction Rule} & \textbf{Concurrency Outcomes} \\ \midrule \endhead InsertEvent & Position-keyed insertion. Concurrent same-position inserts whose durations overlap promote the lexicographically-greater operation to a system-promoted voice (Section~\ref{sec:graph:promoted-voices}) & Voice promotion; non-overlap invariant preserved by allocation. \\ DeleteEvent & Tombstone the event and contained pitch identifiers. Apply tuplet compensation from the operation. Re-anchor referencing structures & Delete-wins against concurrent field modifications on the same event; concurrent same-target deletes are idempotent. \\ RespellPitch & Field overwrite by canonical reduction order; the later operation wins. Earlier respellings of the same pitch are preserved in the conflict record if they differ & Conflict record records the losing spelling for user inspection. \\ Transpose & Apply in canonical reduction order. Pitch identifiers preserved. Operations do not commute & Reduction order decides; conflict record entries for non-trivial intervals only when explicit dependency rules require. \\ CreateCrossCutting & Set union. Concurrent additions of distinct cross-cutting structures all succeed & Tombstoned endpoints cause re-anchoring or cascade-delete per kind. \\ ChangeRegionTimeModel & Structural migration. Contained events whose coordinate kinds become incompatible cause the operation to conflict & Concurrent contained edits authored before the migration may conflict; explicit rebase rules per migration kind. \\ SetUserSystemBreak & LwwAdvisory on per-location break preference & Last-writer-wins on the break state at the same anchor. \\ \bottomrule \end{longtable} \section{Representative Operations} \label{sec:semops:examples} The remainder of the chapter specifies a representative selection of operations, one from each major category. The full catalog is delivered as a separate conformance specification. These examples illustrate the contract that every operation in the catalog \MUST{} satisfy. Each operation specification includes its reduction rule under the canonical reduction model (Section~\ref{sec:semops:reduction}). \subsection{InsertEvent} \label{sec:semops:insertevent} \textbf{Category:} Event operations. \begin{lstlisting}[language=Rust] pub struct InsertEventOp { /// The voice the event is inserted into. pub voice: VoiceId, /// Position within the voice. pub position: EventPosition, /// The event to insert. Its EventId and any contained PitchIds /// are minted at operation-authoring time as a single causal /// step. The event's voice field must match the target voice. pub event: Event, } \end{lstlisting} \textbf{Invariant preconditions:} \begin{itemize} \item The target voice exists. \item The event's \texttt{voice} field equals the target voice's id. \item The event's \texttt{id} is not present elsewhere in the arena. \item Every \texttt{PitchId} contained in the event (in an \texttt{IdentifiedPitch}, a trajectory endpoint, or a stepwise trajectory shape) is unique within the score's pitch-identity index. \item The position is within the enclosing region's time extent. \item The event's position and duration variants agree with the enclosing region's time model. \item Inserting at the position does not produce an event overlap within the voice. Concurrent inserts that would otherwise produce overlap are resolved per the reduction rule below. \end{itemize} \textbf{Advisory preconditions (authoring mode only):} \begin{itemize} \item For pitched events, every pitch is within the instrument's declared range, if any. \item The event's duration does not extend past a region boundary in a way that would require splitting. \end{itemize} \textbf{Effect:} The event is added to the arena; the voice's event list is updated to include the event in sorted position. Every contained \texttt{PitchId} is registered as live in the pitch-identity index. Any attachments declared on the event (spellings, decompositions) are added to their respective indexes. \textbf{Postconditions:} \begin{itemize} \item The voice's events list remains sorted and non-overlapping. \item The event arena's lookup index resolves the new event. \item The event time index includes the new event for its region and voice. \item Every contained pitch is resolvable through the pitch-identity index. \end{itemize} \textbf{Inverse:} \texttt{DeleteEvent} of the inserted event, with captured state equal to the event itself plus any attachments added. \textbf{Reduction rule:} Position-keyed insertion with voice promotion on collision. Concurrent inserts at the same position whose durations would overlap resolve as follows: the operation with the lexicographically-greater \texttt{OperationId} is placed in a newly allocated voice on the same staff instance, with \texttt{VoiceOrigin::SystemPromoted} (Section~\ref{sec:graph:promoted-voices}). The new voice's \texttt{VoiceId} is derived deterministically from the staff instance, the original target voice, and the two operations' identifiers, so all replicas independently allocate the same promoted voice. The non-overlap invariant is preserved by allocation rather than by rejection. Subsequent user normalization operations can merge the promoted voice back into the target, rename it, or accept it as an intentional second voice. \subsection{DeleteEvent} \label{sec:semops:deleteevent} \textbf{Category:} Event operations. \begin{lstlisting}[language=Rust] pub struct DeleteEventOp { pub event: EventId, /// Compensation for tuplet membership, if any. Required when the /// target event is a member of one or more tuplets. pub tuplet_compensation: TupletCompensation, } pub enum TupletCompensation { /// Target is not in any tuplet; no compensation required. NotInTuplet, /// Replace the deleted event with a rest of the same duration. /// The rest receives a freshly-minted EventId; the deleted /// event's EventId and PitchIds are tombstoned. ReplaceWithRest { new_rest: Rest }, /// Rewrite the enclosing tuplet(s) to remain structurally /// consistent. The v1 payload names the affected tuplets by /// id only; it does not carry rewritten values (see the /// precondition below). RewriteTuplets { tuplets: Vec }, /// Cascade-delete the entire tuplet group(s) containing the /// target event. The listed tuplet ids are cascaded. CascadeDeleteTuplets { tuplets: Vec }, } \end{lstlisting} \textbf{Invariant preconditions:} \begin{itemize} \item The target event exists in the arena (i.e., is live, not tombstoned). \item If the target event belongs to one or more tuplets, the \texttt{tuplet\_compensation} field \MUST{} be a variant other than \texttt{NotInTuplet}, and the chosen compensation \MUST{} preserve the tuplet structural consistency invariant (Section~\ref{sec:time:tuplets}) after the delete is applied. \item If the target event does not belong to any tuplet, the \texttt{tuplet\_compensation} field \MUST{} be \texttt{NotInTuplet}. \item For \texttt{ReplaceWithRest}: the replacement rest's duration \MUST{} equal the deleted event's duration; the rest's voice and position \MUST{} match the deleted event. \item For \texttt{RewriteTuplets}: the v1 payload carries only tuplet ids, not the rewritten ratio and member values a reducer would need to demonstrate that the resulting structure preserves the tuplet consistency invariant. Graph-aware reduction \MUST{} therefore refuse the variant as an ill-formed compensation (a precondition no-op) rather than fabricate rewritten values. A future payload revision carrying full rewrites (per-tuplet ratio and members, each of whose member-duration sums \MUST{} match the tuplet's structurally-required total) reopens the variant; until then \texttt{ReplaceWithRest} and \texttt{CascadeDeleteTuplets} are the applicable compensations. \item For \texttt{CascadeDeleteTuplets}: every tuplet listed \MUST{} contain the target event as a member. \end{itemize} \textbf{Effect:} The event is removed from the arena and from its voice's event list. The event's \texttt{EventId} and every contained \texttt{PitchId} are tombstoned in their respective identity indexes: they remain resolvable to ``deleted'' markers and \MUSTNOT{} be reused for new objects. The chosen tuplet compensation is applied in the same delta: a replacement rest is inserted or the tuplets are cascade-deleted as specified (a \texttt{RewriteTuplets} compensation is refused at precondition check; see above). Cross-cutting structures referencing the event or its pitches are re-anchored per the rule table (Section~\ref{sec:semops:reanchor}). Live attachments targeting the event or its pitches transition to tombstoned-target state and are preserved for replay, undo, and historical reference; they are not silently deleted. \textbf{Postconditions:} \begin{itemize} \item The event is no longer live in the arena. \item Every cross-cutting structure that referenced the event has been re-anchored or cascade-deleted. \item The event's identifier and every contained pitch identifier are tombstoned and resolvable only to a deletion record. \item The tuplet structural consistency invariant holds for every tuplet that was previously consistent. \end{itemize} \textbf{Inverse:} \texttt{InsertEvent} reconstructing the event from captured state, re-promoting the tombstoned identifiers to live, reversing the tuplet compensation (delete the replacement rest; restore the original tuplet structure; un-cascade tuplet deletions), and re-creating any cross-cutting structures that were cascade-deleted as a consequence. Re-promotion preserves all stable identifiers; new identifiers \MUSTNOT{} be minted by the inverse. \textbf{Reduction rule:} Set intersection with delete-wins. Concurrent deletion and modification of the same event resolves to deletion; the modification is dropped. Concurrent re-anchoring on the same referent resolves by the deterministic total ordering specified in Section~\ref{sec:semops:reanchor}. \begin{rationale} Requiring explicit tuplet compensation prevents primitive deletion from leaving the score in a state that violates the tuplet structural consistency invariant. The compensation is part of the same delta as the deletion, so concurrent observers see either the pre-delete state or a fully consistent post-delete state, never an intermediate broken state. \end{rationale} \subsection{CreateCrossCutting (Slur)} \textbf{Category:} Cross-cutting structure operations. \begin{lstlisting}[language=Rust] pub struct CreateCrossCuttingOp { pub structure: CrossCuttingStructure, } pub enum CrossCuttingStructure { Slur(Slur), Tie(Tie), Beam(Beam), Spanner(Spanner), Marker(Marker), Repeat(RepeatStructure), Analytical(AnalyticalAnnotation), Comment(Comment), GraphicGesture(GraphicGesture), } \end{lstlisting} The enum above is the data model's full cross-cutting family; the \emph{wire} vocabulary the cross-cutting operations admit at this revision (\texttt{CrossCuttingValue}, Binary Format companion) is the four-kind subset \texttt{Tie}/\texttt{Slur}/\texttt{Beam}/% \texttt{Spanner}. A \texttt{RepeatStructure} is authored by the dedicated \texttt{CreateRepeatStructure}/% \texttt{DeleteRepeatStructure} pair (Operation Catalog \sectionsc{Repeat Structures}; ratified schema-major-2 revision), not through \texttt{CreateCrossCutting}; the remaining variants await their own authoring primitives. \textbf{Invariant preconditions} (Slur case): \begin{itemize} \item The slur's id is unique within the cross-cutting registry. \item The slur's start and end events both exist. \item The start and end events are in the same voice. \item The start event precedes (or equals) the end event in voice order. \end{itemize} \textbf{Advisory preconditions} (Slur case): \begin{itemize} \item The slur does not span a region boundary, unless explicitly permitted by region configuration. \end{itemize} \textbf{Effect:} The slur is added to the cross-cutting registry's slur list. The cross-cutting reference index is updated. \textbf{Postconditions:} \begin{itemize} \item The slur is queryable by id and by referenced events. \end{itemize} \textbf{Inverse:} \texttt{DeleteCrossCutting} of the created slur. \textbf{Reduction rule:} Set union. Concurrent creation of multiple slurs over overlapping ranges is valid: both slurs exist. \subsection{RespellPitch} \textbf{Category:} Pitch and tuning operations. \begin{lstlisting}[language=Rust] pub struct RespellPitchOp { pub pitch: PitchId, pub new_spelling: PitchSpelling, } \end{lstlisting} \textbf{Invariant preconditions:} \begin{itemize} \item The target pitch exists in some event in the arena (i.e., is live, not tombstoned). \item The new spelling is valid in the active pitch space: every accidental in the spelling's \texttt{accidentals} stack belongs to the active accidental registry, the nominal exists in the space's nominal registry, and the octave is in range. \item Duplicate accidentals in the spelling's stack are absent unless the accidental's registered combination semantics explicitly permit repetition. \item The new spelling represents the same scale position as the pitch's current spelling, or is enharmonically equivalent under the active tuning system. (Respelling does not change sounding pitch.) \end{itemize} \textbf{Effect:} A spelling attachment with source \texttt{UserChosen} is created (or replaces an existing \texttt{UserChosen} attachment) targeting the pitch. The spelling attachment index is updated. The pitch's \texttt{PitchId} is unaffected. \textbf{Postconditions:} \begin{itemize} \item Spelling resolution for the pitch yields the new spelling, subject to the precedence configuration. \item The pitch's scale position is unchanged. \item The pitch's acoustic realization is unchanged. \item The pitch's \texttt{PitchId} is unchanged. \end{itemize} \textbf{Inverse:} A \texttt{RespellPitch} restoring the prior spelling (captured), or a \texttt{RemoveAttachment} if no prior \texttt{UserChosen} attachment existed. \textbf{Reduction rule:} Field overwrite by canonical reduction order. Concurrent respellings of the same live pitch resolve to the operation later in canonical order; the materialized spelling is that operation's choice. The losing respelling is recorded in a \texttt{ConflictRecord} of kind \texttt{StructuralFieldCollision} if the two respellings represent different spellings (i.e., differ in nominal, accidental stack, or octave). The per-operation effect assignment follows the general field-collision rule (Requirement~\ref{req:semops:field-collision-effect}): the later operation --- the winner, which materializes --- reads \texttt{Conflicted}, and the earlier operation retains \texttt{Applied}. Identical concurrent respellings reduce idempotently: the later operation produces \texttt{NoOp\{reason: AlreadyApplied\}} with no conflict recorded. \subsection{ChangeRegionTimeModel} \textbf{Category:} Region operations. \begin{lstlisting}[language=Rust] pub struct ChangeRegionTimeModelOp { pub region: RegionId, pub new_time_model: RegionTimeModel, pub position_remapping: PositionRemapping, } pub enum PositionRemapping { /// Preserve absolute time positions where possible. Events that /// cannot be remapped (e.g., metric events under a proportional /// model with no implied tempo) are converted by the supplied /// conversion function. PreserveTime { fallback: PositionConverter }, /// Discard event positions and reinsert by user-provided /// position mapping. Reassign(HashMap), } \end{lstlisting} \textbf{Invariant preconditions:} \begin{itemize} \item The target region exists. \item The new time model is internally consistent. \item The position remapping covers every event in the region, or the remapping is \texttt{PreserveTime} with a defined fallback. \end{itemize} \textbf{Effect:} The region's \texttt{time\_model} is replaced. Every event in the region has its \texttt{position} field updated according to the remapping. The tempo map's applicability is recomputed. \textbf{Postconditions:} \begin{itemize} \item Every event in the region has a valid position in the new time model. \item The region's invariants (events sorted, non-overlapping, within time extent) hold. \end{itemize} \textbf{Inverse:} \texttt{ChangeRegionTimeModel} restoring the prior time model, with a remapping captured from the pre-state. \textbf{Reduction rule:} Structural migration. The operation is applied at its canonical position. Contained events whose coordinate kinds become incompatible with the new time model after migration cause the operation to reduce to \texttt{Conflicted} with a \texttt{ConflictKind::TimeModelMigrationFailure} record listing the incompatible events; the migration does not apply. Concurrent operations on the same region that author events in the prior time model and reduce after the migration may themselves reduce to \texttt{Conflicted} if their coordinate kinds disagree with the new model. Concurrent same-target migrations resolve by canonical order; the earlier operation applies and the later operation reduces to \texttt{Conflicted} with a structural-field-collision conflict (the two migrations are not generally commutable). \begin{rationale} Time-model migration is a structural rebase, not a field set. Treating it as LWW would silently destroy contained musical content whose coordinate kinds depend on the prior time model. Conflicted reductions force the user to address the incompatibility explicitly. \end{rationale} \subsection{Transpose} \textbf{Category:} Pitch and tuning operations. This is a compound operation, defined as a sequence of primitives, listed here as an illustration of compounds. \textbf{User-facing parameters:} \begin{itemize} \item Target scope (events, voice, staff, region, or selection). \item Interval (diatonic, chromatic, or compound). \item Spelling policy (preserve user spellings, respell to fit a target key, or apply a custom rule). \item Tuning policy (preserve, recompute under new tuning, or apply offsets). \end{itemize} \textbf{Primitive decomposition:} A transpose operation expands into: \begin{enumerate} \item For each target pitch, a \texttt{ReplaceEvent} updating the inner \texttt{Pitch} values while preserving every \texttt{IdentifiedPitch}'s \texttt{PitchId}. Transposition does not mint new pitch identifiers; it rewrites the contents of existing identified pitches. \item For each pitch whose spelling is recomputed, a \texttt{RespellPitch} targeting the unchanged \texttt{PitchId} with source \texttt{Propagated}. \item Where the transposition crosses a clef boundary, optional \texttt{ChangeStaffConfiguration} operations to insert clef changes. \end{enumerate} The compound operation is recorded as a single transaction. Each primitive within the transaction has its own re-anchoring and reduction semantics. Identifier preservation ensures that attachments, analytical layers, and cross-cutting references targeting the transposed pitches continue to resolve correctly after the operation. \subsection{SetUserSystemBreak} \textbf{Category:} Layout-semantic operations. \begin{lstlisting}[language=Rust] pub struct SetUserSystemBreakOp { pub anchor: TimeAnchor, pub region: RegionId, pub present: bool, } \end{lstlisting} \textbf{Invariant preconditions:} \begin{itemize} \item The region exists and is staff-based. \item The anchor resolves within the region. \end{itemize} \textbf{Effect:} The region's \texttt{user\_system\_breaks} list is updated to include or exclude the anchor. Layout caches that depend on system breaks are invalidated. \textbf{Postconditions:} \begin{itemize} \item The region's user system breaks list reflects the change. \item Layout output, if produced subsequently, accounts for the change subject to the engraver's override rules (Chapter~\ref{ch:layout-ir}). \end{itemize} \textbf{Inverse:} \texttt{SetUserSystemBreak} with the opposite \texttt{present} value. \textbf{Reduction rule:} LwwAdvisory on per-anchor break preference. Two concurrent operations setting the same break to \texttt{true} reduce idempotently. Concurrent set-true and set-false on the same anchor resolve by canonical reduction order; the later operation's preference is materialized. No conflict record is produced; the break preference is an advisory field eligible for LWW reduction (Section~\ref{sec:semops:lww}). \section{Operation Catalog Conformance} \label{sec:semops:catalog} This chapter specifies the framework. The full catalog of operations is delivered as a separate conformance specification that extends this chapter. \begin{requirement} A conforming implementation \MUST{} provide every operation enumerated in the conformance catalog. Each operation \MUST{} satisfy the framework requirements stated in this chapter: \begin{itemize} \item Declared preconditions classified as invariant or advisory. \item Specified effect on the score graph. \item Specified postconditions, including which invariants are preserved. \item A defined inverse operation. \item A declared reduction rule under the canonical reduction model (Section~\ref{sec:semops:reduction}). \item Specified re-anchoring behavior for any deletions or moves it triggers. \end{itemize} \end{requirement} \begin{openquestion} The conformance catalog is presently under development as a separate document. It is expected to comprise approximately 60--80 primitive operations and an open-ended set of compound operations. The catalog is normative once published; this specification is non-final until the catalog is delivered. \end{openquestion} \section{Forward References} \begin{itemize} \item Operation envelope storage on disk (envelope block layout, causal-summary metadata, the operation index, snapshot canonical-base semantics) is specified in Chapter~\ref{ch:format}. \item Conflict-resolution algorithms (how a \texttt{ResolveConflict} operation transitions a conflict's state, and the full conflict-record contract) are specified in this chapter (Section~\ref{sec:semops:conflicts}) and in the Operation Catalog companion (Appendix~\ref{app:deferred}). \item The interaction between layout-semantic operations (system breaks, page breaks, engraving overrides) and the layout pipeline is specified in Chapter~\ref{ch:layout-ir}. \item The conformance catalog of all operations is a separate deliverable that extends this chapter. \end{itemize} % =========================================================================== \chapter{Layout Intermediate Representation} \label{ch:layout-ir} This chapter specifies the layout intermediate representation (Layout IR): the data structures and pipeline that transform the score graph into a typeset visual layout. The IR sits between the score graph and two downstream consumers: the constraint solver (Chapter~\ref{ch:solver}), which resolves spacing and positioning, and the renderer (specified outside this document), which produces final visual output (pixels, PDF, SVG). The IR is a pipeline of distinct stages, each with its own type and each with a well-defined contract for the next. The pipeline is the spine of the engraving system; the engraving algorithms themselves (beam grouping, slur curvature, accidental ordering, casting-off) are the consumers and producers of stages and are specified outside this document as engraving-algorithm specifications layered atop this IR. \section{Design Principles} \label{sec:layoutir:principles} \begin{description} \item[Pipeline of stages.] The transformation from score graph to visual output is a pipeline of four IR stages: \texttt{LogicalLayoutIR} (structural projection without spatial information), \texttt{ConstrainedLayoutIR} (with constraint inputs but unresolved positions), \texttt{ResolvedLayoutIR} (with all positions resolved), and \texttt{RenderIR} (renderer-bound primitives, defined only at the interface level here). Each stage has its own type. Compilation between stages is unidirectional. \item[Shared provenance.] Every IR object at every stage carries a reference back to the score graph object that originated it. This enables selection, editing back-references, error reporting, and incremental layout invalidation. \item[Engraving decisions are explicit.] When the engraver makes a decision (stem direction, accidental ordering, beam consolidation), the decision is recorded in the IR. The decision can be inspected, overridden, and traced. \item[Overrides exist in both worlds.] User-set engraving overrides are authoritative in the score graph and projected into the IR during the logical stage. The IR may also generate transient overrides for downstream stages. \item[Spring-based spacing.] Time-to-space projection produces elastic spring parameters per time slot. The constraint solver resolves springs system-wide. This applies to both horizontal and vertical layout. \item[Staff-space units, f32 precision.] Spatial coordinates within the IR are expressed in staff spaces using single-precision floating point. Conversion to absolute units (points, millimeters) occurs only at the render boundary. \item[Glyph metrics live elsewhere.] The IR references glyphs by identifier and queries metrics from a font catalog. Metrics are not embedded in IR objects. \item[Incremental layout, fine-grained.] The IR supports incremental re-engraving via per-object dependency tracking on score graph objects. Edits invalidate only the layout objects whose dependencies changed. \item[Uniform region containers.] Staff-based, free-graphic, and hybrid regions produce structurally identical \texttt{LayoutRegion} containers with differing time projections and content kinds. \end{description} \section{Spatial Primitives} \label{sec:layoutir:spatial} \subsection{Units} The IR uses two unit types: \begin{lstlisting}[language=Rust] /// Staff space: the fundamental unit of music engraving. /// One staff space equals the distance between adjacent staff lines. #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] pub struct StaffSpace(pub f32); /// Point: 1/72 inch. Used at the render boundary for absolute /// dimensions (page sizes, font sizes, output coordinates). #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] pub struct Point(pub f32); /// Scaling context for converting between staff spaces and points. pub struct ScaleContext { /// Points per staff space. Depends on the staff size chosen for /// the score; typical values are 4--10 points per staff space. pub points_per_staff_space: f32, } \end{lstlisting} \begin{requirement} Spatial coordinates within the IR (in stages \texttt{LogicalLayoutIR}, \texttt{ConstrainedLayoutIR}, and \texttt{ResolvedLayoutIR}) \MUST{} be expressed in staff spaces. Conversion to points occurs at the \texttt{RenderIR} boundary. Single-precision floating point (\texttt{f32}) \MUST{} be used for IR coordinates. Engraving precision requirements are bounded by printer resolution (typically 600--2400 DPI); single precision provides over 7 significant figures, comfortably exceeding any engraving precision requirement. \end{requirement} \subsection{Geometric Types} \begin{lstlisting}[language=Rust] #[derive(Copy, Clone, Debug)] pub struct Point2D { pub x: StaffSpace, pub y: StaffSpace, } #[derive(Copy, Clone, Debug)] pub struct Size2D { pub width: StaffSpace, pub height: StaffSpace, } #[derive(Copy, Clone, Debug)] pub struct Rect { pub origin: Point2D, pub size: Size2D, } #[derive(Copy, Clone, Debug)] pub struct BoundingBox { pub left: StaffSpace, pub right: StaffSpace, pub top: StaffSpace, pub bottom: StaffSpace, } #[derive(Copy, Clone, Debug)] pub struct Transform2D { pub matrix: [[f32; 3]; 3], } \end{lstlisting} \section{Provenance} \label{sec:layoutir:provenance} Every IR object carries a provenance record tracing it back to the score graph object that originated it. Provenance is uniform across all four stages; provenance records are shared structurally between stages so that an object passing through the pipeline retains a consistent identity. \begin{lstlisting}[language=Rust] pub struct Provenance { /// The primary source: the score graph object this IR object /// represents or derives from. pub source: TypedObjectId, /// For IR objects that derive from synthesizing engraving /// decisions rather than directly from score graph objects, the /// synthesis kind. Examples: an automatically inserted natural /// sign, a generated rest, a system break. pub synthesis: Option, /// Additional source dependencies. An IR object may depend on /// multiple score graph objects (e.g., a beam depends on its /// member events). All dependencies are tracked for /// incremental layout. pub dependencies: Vec, /// IR object's stable identifier across re-layouts where the /// underlying source is unchanged. pub stable_id: LayoutObjectId, } pub enum SynthesisKind { /// An accidental inserted to cancel a prior alteration. CancellationAccidental, /// A natural sign generated by key signature rules. KeySignatureNatural, /// A rest inserted to fill a voice gap. GeneratedRest, /// A system or page break selected by the engraver. EngravedBreak, /// A multimeasure rest combining consecutive empty measures. MultimeasureRest, /// A cautionary key or time signature at a system break. Cautionary, /// A custom synthesis declared by a layout extension. Registered(SynthesisRegistryId), } pub struct LayoutObjectId(pub u128); \end{lstlisting} \begin{requirement} Every layout object at every IR stage \MUST{} carry a non-empty \texttt{Provenance} record. Objects whose direct \texttt{source} does not correspond to a score graph object (engraver-synthesized objects) \MUST{} declare a \texttt{synthesis} kind. The \texttt{dependencies} list \MUST{} include every score graph object whose change should invalidate this layout object. \end{requirement} \begin{requirement} \label{req:layoutir:continuation-synthesis} \textbf{System-spanning continuation.} A line primitive (stroke) that spans a system boundary is split into per-system segments; the segments after the first are engraver-synthesized and \MUST{} declare a \texttt{synthesis} kind. The reserved registered kind \texttt{SynthesisKind::Registered(SYSTEM\_CONTINUATION\_SYNTHESIS)} names this continuation. Its \texttt{stable\_semantic\_instance\_key} (per Requirement~\ref{req:layoutir:object-id-derivation}) is the pair \texttt{(original,\,ordinal)} --- the pre-split stroke's object and the segment's order within the split --- which is stable for a fixed set of system breaks. Because \texttt{LayoutObjectId}s are non-canonical and re-derived per layout, this key need only be stable within a layout, not across relayouts that move the breaks. \end{requirement} \begin{requirement} \label{req:layoutir:object-id-derivation} A \texttt{LayoutObjectId} \MUST{} be stable across re-layouts whose underlying source is unchanged. It \MUST{} be derived by domain-separated BLAKE3 truncation with the reserved layout domain tag \texttt{"MUSCLOID"} over a key that depends on how the object is manifested: \begin{itemize} \item A layout object that manifests a single score-graph object exactly once is keyed on \texttt{source.canonical\_bytes()}. \item A multiply-manifested object --- one score-graph object that appears in more than one layout context, e.g. a staff manifested in two regions --- is keyed on the pair \texttt{(source,\,region)}, so each manifestation receives a distinct, stable id. \item A synthesized object (no direct score-graph \texttt{source}; \texttt{synthesis} is \texttt{Some}) is keyed on the triple \texttt{(source,\,synthesis\_kind,\, stable\_semantic\_instance\_key)}, where the \texttt{stable\_semantic\_instance\_key} distinguishes multiple synthesized objects sharing a source and synthesis kind by a semantically stable discriminator (not a layout-position ordinal, which would not survive relayout). \end{itemize} \texttt{LayoutObjectId}s are \emph{non-canonical}: they are not part of document state and do not enter any content hash, so the \texttt{"MUSCLOID"} tag lives in the layout namespace and is not one of the reserved \emph{canonical} system tags of Section~\ref{sec:graph:system-derived}. The fixed derivation is pinned for incremental-relayout correctness and provenance back-reference stability; its consumers are the solver and renderer (Track~A), not the interchange track. Because these ids never enter document state, no stored or interchanged artifact depends on this derivation. The v0 reference implementation realizes this derivation as of Pass-12 item P12-I2: \texttt{epiphany-determinism} reserves the built-in \texttt{"MUSCLOID"} layout tag and the \texttt{layout-ir} provenance derivations (single, multiply-manifested, and synthesized) route through it, the synthesized case no longer borrowing \texttt{"MUSCCONF"}. Because the ids are non-canonical, doing so changed layout-id values but no stored or interchanged artifact. \end{requirement} \section{The Stage Pipeline} \label{sec:layoutir:pipeline} \subsection{Pipeline Overview} \begin{lstlisting} ScoreGraph | | Engraving pass: | - Project structure | - Make engraving decisions (stems, beams, accidentals) | - Apply user overrides v LogicalLayoutIR | | Spacing pass: | - Compute spring parameters per time slot | - Compute glyph bounding boxes | - Build collision constraints v ConstrainedLayoutIR | | Constraint solving (Chapter 9): | - Resolve horizontal positions | - Resolve vertical positions | - Resolve page and system breaks v ResolvedLayoutIR | | Render projection (out of scope): | - Convert to renderer primitives | - Apply page templates | - Generate draw calls v RenderIR \end{lstlisting} \subsection{Stage Contracts} \begin{requirement} Each pipeline stage \MUST{} accept its input stage as a pure function: identical inputs (plus identical configuration) \MUST{} produce identical outputs. The stages are deterministic by contract. Stage transitions \MUST{} preserve provenance: every output object \MUST{} carry a \texttt{Provenance} record either inherited from its input or newly synthesized with appropriate \texttt{synthesis} kind. \end{requirement} \section{LogicalLayoutIR} \label{sec:layoutir:logical} \texttt{LogicalLayoutIR} is the structural projection of the score graph into layout objects, with all engraving decisions made but spatial positions unresolved. It is the output of the engraving pass and the input to the spacing pass. \subsection{Top-Level Structure} \begin{lstlisting}[language=Rust] pub struct LogicalLayoutIR { /// Source score graph reference (typically a snapshot pointer /// or version identifier). pub source: ScoreVersion, /// Layout regions, one per score graph region. pub regions: Vec, /// Engraving decisions made during this pass. pub engraving_decisions: Vec, /// User overrides projected from the score graph. pub overrides: Vec, /// Cross-region layout objects (slurs spanning regions, etc.). pub cross_region: Vec, } \end{lstlisting} \subsection{Layout Regions} \begin{lstlisting}[language=Rust] pub struct LayoutRegion { pub provenance: Provenance, /// Coordinate system local to this region. The region's /// transform maps local coordinates to canvas coordinates. pub coordinate_system: LocalCoordinateSystem, /// Time axis: how time positions map to horizontal positions. /// A tagged union over the three built-in axis kinds plus a /// registered variant for extension-defined axes. The enum /// form is canonical for serialization, hashing, and /// conformance comparison; implementations MAY use a trait /// object internally for dynamic dispatch. pub time_axis: TimeAxisModel, /// Vertical extent: the set of staff bands this region occupies. pub vertical_extent: VerticalExtent, /// Layout objects within this region. pub objects: Vec, } /// Canonical representation of a region's time axis. Variants /// correspond to the three built-in region time models; the /// Registered variant carries extension-defined axes by typed /// registry id and serialized payload. pub enum TimeAxisModel { Metric(MetricTimeAxis), Proportional(ProportionalTimeAxis), Aleatoric(AleatoricTimeAxis), Registered(TimeAxisRegistryId, SerializedRegisteredAxis), } \end{lstlisting} \subsection{The Time Axis Trait} For dynamic dispatch and ergonomic implementation, the \texttt{TimeAxisModel} enum's payload types share a trait: \begin{lstlisting}[language=Rust] pub trait TimeAxis: Send + Sync { /// The time model kind this axis implements. fn kind(&self) -> TimeAxisKind; /// Project a time position to a horizontal spring slot. /// Returns a slot identifier; spring parameters are queried /// separately. fn project(&self, time: TimePoint) -> SpringSlotId; /// Enumerate the spring slots in time order. fn slots(&self) -> &[SpringSlotId]; /// For incremental layout: which slots are affected by a change /// to the given time range? fn affected_slots(&self, range: TimeRange) -> Vec; } pub enum TimeAxisKind { Metric, Proportional, Aleatoric, Registered(TimeAxisRegistryId), } pub enum TimePoint { Musical(MusicalPosition), WallClock(WallClockTime), } pub enum TimeRange { Musical { start: MusicalPosition, end: MusicalPosition }, WallClock { start: WallClockTime, end: WallClockTime }, } \end{lstlisting} The three built-in implementations: \begin{lstlisting}[language=Rust] pub struct MetricTimeAxis { /// Measure boundaries and their projected spring slots. pub measures: Vec, /// Per-time-slot spring parameters, indexed by slot id. pub slots: Vec, } pub struct ProportionalTimeAxis { /// Wall-clock duration of the region. pub duration: WallClockDuration, /// Linear horizontal projection: space per second. pub space_per_second: StaffSpace, pub slots: Vec, } pub struct AleatoricTimeAxis { /// DAG of event ordering. pub ordering: EventOrderingDAG, /// Spring slots are organized by topological-order layers. pub slots: Vec, } \end{lstlisting} \subsection{Layout Objects} A \texttt{LayoutObject} is the unit of engraving. At the \texttt{LogicalLayoutIR} stage, layout objects are composite: a single \texttt{Note} object contains its notehead, stem, flag, accidental, articulations, and so on. At the \texttt{ConstrainedLayoutIR} stage these are flattened into individual glyph objects. \begin{lstlisting}[language=Rust] pub enum LayoutObject { /// Composite note object: notehead(s), stem, flag, accidental(s), /// articulations, ornaments, dot(s). Note(NoteLayout), /// Composite chord object: multiple noteheads sharing stem and /// articulations. Chord(ChordLayout), /// Rest with its glyph kind (whole, half, quarter, ...). Rest(RestLayout), /// Beam group: multiple notes/chords beamed together. BeamGroup(BeamGroupLayout), /// Tuplet visual: bracket and number. TupletDisplay(TupletDisplayLayout), /// Slur or phrase mark: control points and style. Slur(SlurLayout), /// Tie: same as slur but connecting same-pitch events. Tie(TieLayout), /// Spanner: hairpins, octave lines, pedal lines, etc. Spanner(SpannerLayout), /// Marker: rehearsal marks, section labels, tempo marks. Marker(MarkerLayout), /// Bar line: simple, double, final, repeat, etc. BarLine(BarLineLayout), /// Clef, key signature, time signature glyphs. Clef(ClefLayout), KeySignature(KeySignatureLayout), TimeSignatureDisplay(TimeSignatureDisplayLayout), /// Staff: the staff lines themselves. Staff(StaffLayout), /// Text: lyrics, dynamics text, expressive text. Text(TextLayout), /// Free graphic: path, shape, image, stroke. Graphic(GraphicLayout), /// Multimeasure rest: consolidated empty measures. MultimeasureRest(MultimeasureRestLayout), /// Cue: small-print rendering of source material. Cue(CueLayout), /// Trajectory: glissando, portamento line. Trajectory(TrajectoryLayout), /// Generic group: implementation-defined. Group(GroupLayout), } \end{lstlisting} \subsection{Note Layout: A Worked Example} To illustrate the composite-object pattern at the logical stage: \begin{lstlisting}[language=Rust] pub struct NoteLayout { pub provenance: Provenance, /// Reference to the score graph event. pub event: EventId, /// The notehead glyph and its staff position. pub notehead: NoteheadLayout, /// The stem, if any. pub stem: Option, /// Flag, if any (single-note events with eighth-or-shorter /// duration that are not part of a beam group). pub flag: Option, /// Accidental glyphs attached to the note, in stacking order. pub accidentals: Vec, /// Augmentation dots. pub dots: Vec, /// Articulations attached to the note. pub articulations: Vec, /// Ornament glyphs. pub ornaments: Vec, /// Ledger lines required to display the note's staff position. pub ledger_lines: Vec, /// Engraving decisions made for this note. pub decisions: NoteDecisions, } pub struct NoteDecisions { pub stem_direction: StemDirection, pub accidental_visible: bool, pub accidental_parenthesized: bool, pub notehead_shape: NoteheadShape, /// Voice membership influences default stem direction; /// overrides may change it. pub stem_direction_source: DecisionSource, } pub enum DecisionSource { /// Decision derived from automatic engraving rules. Automatic, /// Decision derived from a user override in the score graph. UserOverride(EngravingOverrideId), /// Decision derived from an IR-stage override. IrOverride, } \end{lstlisting} \subsection{Engraving Decisions} The pipeline records engraving decisions explicitly so they can be inspected, overridden, and replayed. \begin{lstlisting}[language=Rust] pub struct EngravingDecision { pub id: EngravingDecisionId, pub target: LayoutObjectId, pub kind: EngravingDecisionKind, pub source: DecisionSource, } pub enum EngravingDecisionKind { StemDirection(StemDirection), BeamGrouping(BeamGroupingChoice), AccidentalOrdering(Vec), SlurDirection(SlurDirection), LedgerLineCount(u8), EnharmonicSpelling(PitchSpelling), SystemBreak, PageBreak, NoteheadShape(NoteheadShape), RestPosition(StaffPosition), // ... extensible } \end{lstlisting} \section{Engraving Overrides} \label{sec:layoutir:overrides} Engraving overrides are user assertions that the engraver's default decision should be replaced. Overrides are authoritative in the score graph and projected into the IR during the logical stage. \subsection{Override Definition} \begin{lstlisting}[language=Rust] pub struct EngravingOverride { pub id: EngravingOverrideId, /// What this override targets. May reference a score graph object /// (typical) or an IR-synthesized object (transient overrides). pub target: OverrideTarget, /// The override itself. pub kind: OverrideKind, /// Binding strength. pub priority: OverridePriority, /// Provenance: who set the override and when. pub origin: OverrideOrigin, } pub enum OverrideTarget { /// Targets a score graph object. Authoritative; survives reload. ScoreGraph(TypedObjectId), /// Targets an IR-synthesized object. Transient; cleared on /// re-engraving unless explicitly re-applied. IrSynthesized(LayoutObjectId), } pub enum OverrideKind { StemDirection(StemDirection), SlurDirection(SlurDirection), SlurCurvature(CurvatureOverride), AccidentalParenthesized(bool), AccidentalVisible(bool), NoteheadShape(NoteheadShape), BeamGeometry(BeamGeometryOverride), SystemBreak { anchor: TimeAnchor }, PageBreak { anchor: TimeAnchor }, HiddenObject, CustomPosition(Point2D), EnharmonicSpelling(PitchSpelling), LedgerLineSuppression, DotPlacement(DotPlacement), // ... extensible } // System- and page-break overrides address a *position*, not an // object: the break kind carries the break's TimeAnchor, while the // override's ScoreGraph target names the owning region. They are // projected during the logical stage from the score graph's // authoritative user_system_breaks / user_page_breaks lists // (Chapter 5). Break authorship (author, timestamp) lives in the // operation log, not the materialized break lists, so projected // break overrides carry OverrideOrigin::Internal until the // snapshot-undo refinement (P11-C8) surfaces authorship. pub enum OverridePriority { /// The engraver MUST honor this override. Failure produces an /// error rather than a silent override-of-the-override. Hard, /// The engraver SHOULD honor this override. The engraver may /// override it only under documented exceptional conditions /// (system overflow, unresolvable collision); when it does, the /// override is recorded in the resulting decisions with source /// IrOverride. Soft, } pub enum OverrideOrigin { User { author: AuthorId, timestamp: Timestamp }, Import { format: ForeignFormatId }, Plugin { plugin: PluginId }, Internal, } \end{lstlisting} \subsection{Override Resolution} \begin{requirement} Overrides \MUST{} be applied during the engraving pass producing \texttt{LogicalLayoutIR}. Hard overrides \MUST{} be honored or the pass \MUST{} report an error. Soft overrides \MUST{} be honored except where doing so would produce an invalid layout; when a soft override is not honored, the resulting decision \MUST{} record the override and the reason for its non-application. \end{requirement} \begin{requirement} Overrides whose targets reference objects in the score graph (the \texttt{ScoreGraph} variant) \MUST{} be preserved across re-engraving. Overrides targeting IR-synthesized objects (the \texttt{IrSynthesized} variant) are transient; they are cleared on re-engraving unless explicitly re-applied. Implementations \MUST{} not silently promote IR-synthesized overrides to score-graph overrides without explicit user action. \end{requirement} \begin{requirement} \label{req:layoutir:break-origin-attribution} \textbf{Break-override attribution.} When the engraver honours a user break override, the resulting decision \MUST{} carry \texttt{DecisionSource::UserOverride(id)} naming the override. Because a normalized break \emph{constraint} (\texttt{SystemBreakAt} / \texttt{PageBreakAt}, Section~\ref{sec:layoutir:constrained}) carries no override identity, this attribution is threaded through the projection: the \texttt{ConstrainedLayoutIR} carries a \texttt{break\_origins} sidecar, populated when the logical stage projects the break, mapping each user-anchored break slot to its override id. The normalized constraint record is deliberately \emph{not} widened to carry override identity --- attribution is a projection concern, not a constraint-solver input. \end{requirement} \section{ConstrainedLayoutIR} \label{sec:layoutir:constrained} \texttt{ConstrainedLayoutIR} is the output of the spacing pass: the logical IR with composite objects flattened to individual glyphs, each glyph carrying its bounding box, anchor, and constraint inputs to the solver. \begin{requirement} \label{req:layoutir:constraint-floor} \textbf{Minimal-tier constraint-emission floor (ratified Pass 12).} The spacing pass \MUST{} emit at least the following constraint set --- the \emph{Minimal-tier floor}, the testable acceptance surface for the \texttt{Minimal} conformance tier (Chapter~\ref{ch:solver}): no-collision chains between successive notehead-bearing columns; per-glyph containment within the owning region's frame; and one constraint per user break override (system and page). Richer emission sets (inter-band collision, kerned optical adjacency, duration- proportional spring preferences) belong to higher tiers, whose floors are defined when those tiers are ratified. \end{requirement} \subsection{Glyph-Level Objects} \begin{lstlisting}[language=Rust] pub struct GlyphObject { pub provenance: Provenance, /// The glyph to render. Reference into a font catalog; metrics /// are queried from the catalog rather than embedded here. pub glyph: GlyphReference, /// The spring slot this glyph belongs to (horizontal time slot). pub horizontal_slot: SpringSlotId, /// The vertical band this glyph belongs to. pub vertical_band: VerticalBandId, /// Bounding box of the glyph relative to its anchor, in staff /// spaces. Looked up from the font catalog at IR construction /// time and cached here. pub bounding_box: BoundingBox, /// Anchor point: where the glyph's reference position sits /// within its bounding box. pub anchor: Point2D, /// Layer ordering: higher draws on top. pub layer: i32, /// Visual style applied to the glyph. pub style: GlyphStyle, } \end{lstlisting} \subsection{Spring Slots} A spring slot is a time slot's spacing parameters: the constraint solver's input for resolving horizontal positions. \begin{lstlisting}[language=Rust] pub struct SpringSlot { pub id: SpringSlotId, pub time: TimePoint, /// Minimum width: the smallest the slot may compress to. The /// constraint solver MUST NOT compress below this. pub min_width: StaffSpace, /// Preferred width: the natural width under unconstrained /// spacing. Derived from Gourlay-style proportional rules. pub preferred_width: StaffSpace, /// Maximum width, if bounded. None means unbounded above. pub max_width: Option, /// Stretch factor: how readily this slot stretches when more /// width is available than preferred. Higher = more elastic. pub stretch_factor: f32, /// Compress factor: how readily this slot compresses when less /// width is available than preferred. Higher = more compressible. pub compress_factor: f32, /// Glyphs belonging to this slot, ordered by stacking /// considerations (accidentals to left of notehead, etc.). pub members: Vec, } \end{lstlisting} \subsection{Vertical Bands} Vertical layout uses the same spring model. A \emph{vertical band} is a horizontal slice of the canvas (typically a staff or an inter-staff gap) with its own spring parameters. \begin{lstlisting}[language=Rust] pub struct VerticalBand { pub id: VerticalBandId, pub kind: VerticalBandKind, pub min_height: StaffSpace, pub preferred_height: StaffSpace, pub max_height: Option, pub stretch_factor: f32, pub compress_factor: f32, /// Glyphs belonging to this band. pub members: Vec, } pub enum VerticalBandKind { Staff(StaffId), InterStaffGap, InterSystemGap, MarginBand, } \end{lstlisting} \subsection{Constraints} The IR carries explicit constraint inputs for the solver: \begin{lstlisting}[language=Rust] pub struct ConstrainedLayoutIR { pub source: ScoreVersion, pub regions: Vec, /// Horizontal spring slots across all regions. pub horizontal_slots: Vec, /// Vertical bands. pub vertical_bands: Vec, /// Glyph-level layout objects. pub glyphs: Vec, /// Additional constraints not captured by spring parameters. pub constraints: Vec, /// Engraving decisions, carried forward from the logical stage. pub engraving_decisions: Vec, } pub enum LayoutConstraint { /// Two glyphs must not overlap in 2D space. NoCollision { a: GlyphObjectId, b: GlyphObjectId }, /// A glyph must align with another along an axis. Align { a: GlyphObjectId, b: GlyphObjectId, axis: Axis, }, /// A glyph must be positioned within a region of space. PositionWithin { glyph: GlyphObjectId, region: Rect, }, /// A range of slots must end at a system boundary. SystemBreakAt { slot: SpringSlotId, kind: BreakKind }, /// A range of slots must end at a page boundary. PageBreakAt { slot: SpringSlotId, kind: BreakKind }, /// Custom constraint declared by a layout extension. Registered(ConstraintRegistryId, ConstraintParameters), } pub enum BreakKind { Hard, Soft, } \end{lstlisting} \begin{requirement} \label{req:layoutir:break-satisfaction} \textbf{Break-constraint satisfaction.} A \texttt{SystemBreakAt} at \texttt{slot} is \emph{satisfied} iff the final resolved layout starts a system at that slot; a \texttt{PageBreakAt} iff the final layout starts a page there. A slot that already begins a system (respectively page) --- in particular a region's first slot --- is trivially satisfied. Satisfaction is a predicate on the output \texttt{ResolvedLayoutIR}, evaluable by any consumer, not on the solver's internal spring state. A solver's tier claim (Section~\ref{sec:layoutir:resolved}) reports which \texttt{Hard} break constraints its layout satisfies; a \texttt{Soft} break is advisory and MAY be left unsatisfied. \end{requirement} \section{ResolvedLayoutIR} \label{sec:layoutir:resolved} \texttt{ResolvedLayoutIR} is the output of the constraint solver: every glyph, stroke, and curve has a definitive position. This is the IR consumed by the renderer. \begin{lstlisting}[language=Rust] pub struct ResolvedLayoutIR { pub source: ScoreVersion, /// Pages of the resolved score. pub pages: Vec, /// Glyph-level objects with resolved positions. pub glyphs: Vec, /// Non-glyph line primitives (staff lines, stems, barlines, /// volta brackets, ...), positioned alongside the glyphs. pub strokes: Vec, /// Cubic-Bezier curve primitives (slurs, ...), positioned /// alongside the glyphs. pub curves: Vec, /// Engraving decisions, including any made by the constraint /// solver (e.g., soft system breaks placed by casting-off). pub engraving_decisions: Vec, } pub struct ResolvedPage { pub provenance: Provenance, pub number: u32, pub size: Size2D, pub margins: Margins, pub systems: Vec, pub free_objects: Vec, } pub struct ResolvedSystem { pub provenance: Provenance, pub bounding_box: Rect, pub staves: Vec, pub measures: Vec, } pub struct ResolvedGlyph { pub provenance: Provenance, pub glyph: GlyphReference, pub position: Point2D, pub transform: Option, pub bounding_box: BoundingBox, pub style: GlyphStyle, pub layer: i32, } /// A line primitive the solver positions but the vertical-band /// model does not contain (a staff line, stem, barline, or bracket). pub struct Stroke { pub provenance: Provenance, pub from: Point2D, pub to: Point2D, pub thickness: StaffSpace, pub layer: i32, pub style: GlyphStyle, } /// A cubic-Bezier curve primitive (a slur), its four control /// points in `p0`..`p3` drawing order. pub struct Curve { pub provenance: Provenance, pub p0: Point2D, pub p1: Point2D, pub p2: Point2D, pub p3: Point2D, pub thickness: StaffSpace, pub layer: i32, pub style: GlyphStyle, /// The line pattern the renderer strokes the path with /// (a slur's authored `SpanStyle.line`): solid, dashed, or dotted. pub line: LineStyle, } \end{lstlisting} \begin{requirement} \label{req:layoutir:resolved-primitives} \textbf{Non-glyph resolved primitives (ratified schema-major-2, Phase~F).} Beyond glyphs, \texttt{ResolvedLayoutIR} carries non-glyph \emph{line-stroke} primitives (staff lines, stems, barlines, brackets) and \emph{cubic-B\'ezier curve} primitives (slurs). Each \MUST{} carry provenance --- the basis of hit-testing (\ref{sec:layoutir:provenance}) --- and is positioned by the solver like a glyph: a spanning stroke or curve is re-spaced by the same horizontal coordinate map its endpoint columns move under. These primitives are \emph{non-canonical}: like layout identifiers and glyph positions, they are not part of document state and enter no content hash (\ref{req:layoutir:object-id-derivation}). A conformant renderer draws them alongside the glyphs; the renderer encoding itself is out of scope (\ref{sec:layoutir:render}). \end{requirement} \begin{requirement} \label{req:layoutir:repeat-render} \textbf{Minimal-tier repeat and volta rendering (ratified schema-major-2, Phase~F).} A \texttt{RepeatStructure} (\ref{sec:graph:repeats}) whose kind draws barlines (\texttt{SimpleRepeat}, \texttt{Volta}) \MUST{} render a repeat barline at each of its resolved boundaries --- the precomposed repeat sign, either replacing a coinciding measure barline or standing at the boundary column when none coincides, and the repeat-dot pair beside a region-closing final barline (which is never replaced). Each \texttt{Volta} \MUST{} render a bracket spanning its resolved extent, with its ending numbers. A boundary that does not resolve to a spacing column draws \emph{no} ink: a traced anchor preserves the structure's provenance rather than placing a mark at a false position. The jump kinds (\texttt{DaCapo}, \texttt{DalSegno}) draw no Minimal-tier marks --- their segno / coda / instruction text awaits a text primitive --- and a repeat spanning more than one region renders in no single region at this tier. The precise glyph vocabulary and spacing are engraving-algorithm concerns (\ref{sec:layoutir:forward}). \end{requirement} \begin{requirement} \label{req:layoutir:slur-curve} \textbf{Slur rendering (ratified schema-major-2, Phase~F; extended Push~3).} A \texttt{Slur} (\ref{sec:graph:slurs}) \MUST{} render as a cubic-B\'ezier \texttt{Curve} arcing between its two endpoint event columns, honoring an authored \texttt{CurvatureOverride} (direction and apex height) when present and choosing them otherwise. A slur whose endpoint does not resolve to a column on a single staff of one region draws \emph{no} curve: a traced anchor preserves its provenance rather than a floating arc. An authored non-\texttt{Solid} \texttt{SpanStyle} line (dashed, dotted) is rendered faithfully as that pattern (its \texttt{LineStyle} rides the \texttt{Curve}); an implementation that instead defers the pattern \MUST{} surface the deferral (a layout diagnostic), never silently rendering solid. A slur whose span crosses a system break \MUST{} split into per-system sub-curves --- the first carrying the slur's provenance, the rest engraver-synthesized continuations (\ref{req:layoutir:continuation-synthesis}) --- rather than drawing whole in one system. The curvature-computing algorithm remains an engraving-specification concern (\ref{sec:layoutir:forward}). \end{requirement} \section{RenderIR (Interface Only)} \label{sec:layoutir:render} The \texttt{RenderIR} stage is the renderer's input. Its full specification is delivered separately; this section defines only the interface contract. \begin{lstlisting}[language=Rust] pub trait RenderIRProducer { /// Convert resolved IR to renderer-bound primitives. fn produce( &self, resolved: &ResolvedLayoutIR, scale: ScaleContext, config: RenderConfiguration, ) -> RenderIR; } pub struct RenderConfiguration { /// Target output: PDF, SVG, on-screen, print. pub target: RenderTarget, /// Color space and color management options. pub color: ColorConfiguration, /// Anti-aliasing and rasterization options. pub rasterization: RasterizationConfiguration, } \end{lstlisting} \begin{requirement} Implementations producing \texttt{RenderIR} \MUST{} preserve provenance from \texttt{ResolvedLayoutIR}: every renderer primitive \MUST{} be traceable to its originating \texttt{ResolvedLayoutIR} primitive --- a \texttt{ResolvedGlyph}, \texttt{Stroke}, or \texttt{Curve} --- and therefore to its score graph source. This is the basis of hit-testing, selection, and back-reference navigation in the UI. The full RenderIR type and its production rules are specified in the renderer specification, outside the scope of this document. \end{requirement} \section{Incremental Layout and Caching} \label{sec:layoutir:incremental} The pipeline supports incremental layout via per-object dependency tracking. An edit to a score graph object invalidates only the IR objects whose provenance lists it as a dependency. \subsection{The Layout Cache} \begin{lstlisting}[language=Rust] pub struct LayoutCache { /// Reverse index: maps score graph object ids to the layout /// objects depending on them. pub dependencies: DependencyIndex, /// Cached IR stages, partitioned for granular invalidation. pub logical: HashMap, pub constrained: HashMap, pub resolved: HashMap, /// Cached layout-stage outputs at finer granularity for /// performance-critical paths. pub fine_cache: FineLayoutCache, } pub struct DependencyIndex { /// For each score graph object, the layout objects depending /// on it. Reverse-indexed to make invalidation O(k) where k is /// the number of dependents. pub forward: HashMap>, /// For each layout object, its dependencies. Used to verify and /// rebuild the reverse index on cache writes. pub reverse: HashMap>, } \end{lstlisting} \subsection{Invalidation Rules} \begin{requirement} When a semantic operation (Chapter~\ref{ch:semops}) is applied to the score graph, the following invalidation \MUST{} occur: \begin{enumerate} \item Every layout object whose \texttt{Provenance} lists a mutated score graph object in its \texttt{source} or \texttt{dependencies} is invalidated. \item Invalidation propagates: layout objects that depend on invalidated layout objects (via cross-region references, spanning structures, etc.) are also invalidated. \item Invalidated objects are cleared from the cache at their respective stages. \item Invalidated systems trigger casting-off re-evaluation for themselves and any downstream systems whose horizontal extent may shift. \end{enumerate} Re-engraving runs from the first invalidated stage forward, using cached values for non-invalidated objects. \end{requirement} \subsection{Frame-Budget Requirements} \begin{requirement} The incremental layout system \MUST{} support the frame-budget requirements established in Chapter~\ref{ch:perf}: an edit affecting a single system on a 100-page orchestral score \MUST{} re-layout in under 16.7\,ms on the reference hardware profile. Achieving this requires (a) fine-grained dependency tracking as specified above; (b) cached glyph metrics in the font catalog; (c) parallel re-engraving where the affected systems are independent; (d) the constraint solver supporting partial re-resolution of affected slots only (see Chapter~\ref{ch:solver}). \end{requirement} \section{Region Uniformity} \label{sec:layoutir:region-uniformity} Staff-based, free-graphic, and hybrid regions produce structurally identical \texttt{LayoutRegion} containers. The differences are expressed in: \begin{itemize} \item The \texttt{time\_axis}: metric, proportional, or aleatoric. \item The \texttt{objects} mix: staff-based regions contain primarily notation objects; free-graphic regions contain primarily \texttt{Graphic} objects; hybrid regions contain both. \end{itemize} \begin{requirement} All three region kinds \MUST{} use the same \texttt{LayoutRegion} container type. The constraint solver \MUST{} treat regions uniformly: it consumes spring slots and constraints without branching on region kind. \end{requirement} \begin{rationale} Uniform containers permit the constraint solver to be agnostic to region kind, simplifying its implementation. The differences between region kinds are encoded in the \texttt{TimeAxis} implementation and the object mix, not in container structure. This is the same insight that makes the file format and semantic operations layers tractable: structural uniformity at the containers, polymorphism in the contents. \end{rationale} \section{Glyph Catalog Interface} \label{sec:layoutir:catalog} The IR references glyphs by identifier; glyph metrics are queried from a font catalog at IR construction. This section defines the interface; the catalog implementation is outside the core specification. \begin{lstlisting}[language=Rust] pub trait GlyphCatalog: Send + Sync { /// Resolve a glyph reference to its metrics. fn metrics(&self, glyph: &GlyphReference) -> Option; /// Resolve a glyph reference to its rendering data. fn render_data(&self, glyph: &GlyphReference) -> Option; /// SMuFL version supported. fn smufl_version(&self) -> SmuflVersion; } pub struct GlyphMetrics { pub bounding_box: BoundingBox, pub advance_width: StaffSpace, pub anchors: HashMap, } pub struct GlyphRenderData { /// Outline data: path commands suitable for vector rendering. pub outline: Vec, /// Optional pre-rendered bitmap for raster targets. pub bitmap: Option, } \end{lstlisting} \begin{requirement} Implementations \MUST{} use a glyph catalog for metric and render-data lookups during IR construction. Metrics \MUSTNOT{} be duplicated in the IR; the IR carries glyph identifiers and queries the catalog. Implementations \MAY{} cache metrics within IR objects as a performance optimization provided cached values are invalidated when the underlying catalog changes. \end{requirement} \subsection{Glyph Catalog Identity for Layout Conformance} \label{sec:layoutir:catalog-identity} Layout output depends on glyph metrics. For reproducible layout, the exact glyph catalog used \MUST{} be identifiable. \begin{lstlisting}[language=Rust] /// A reproducibility-quality identifier for the glyph catalog /// used to produce a layout. Required for any layout conformance /// claim that depends on byte-equal output across runs. pub struct GlyphCatalogIdentity { /// SMuFL version targeted. pub smufl_version: SmuflVersion, /// Identifier of the specific font in use (e.g., Bravura, /// Petaluma, Leland). pub font_id: FontId, /// Optional semantic version of the font, if the font /// publisher uses versioned releases. pub font_version: Option, /// Content hash of the metric data made available to the /// layout solve. The hash is over a canonical serialization /// of every glyph's metrics (bounding box, advance width, /// named anchors) for every glyph referenced by the /// ConstrainedLayoutIR delivered to the solver, including /// glyphs that are candidates but do not appear in the final /// ResolvedLayoutIR. Computed by BLAKE3 with the domain /// tag "MUSCFNTM". pub metrics_hash: ContentHash, } \end{lstlisting} \begin{requirement} Any layout conformance claim subject to byte-equal output (within-implementation determinism per Chapter~\ref{ch:solver}) \MUST{} declare the \texttt{GlyphCatalogIdentity} under which it was produced. Two layouts produced with different \texttt{metrics\_hash} values are layouts under different inputs; byte equality is not expected. Implementations that cannot identify their glyph catalog at this granularity \MUST{} mark their layout output as \emph{implementation-specific} or \emph{non-canonical}; conformance suites \MUSTNOT{} accept such output as byte-equivalent to a reference solver's output. The \texttt{metrics\_hash} is computed over the glyphs that are \emph{inputs} to the layout solve (those referenced by the \texttt{ConstrainedLayoutIR}), not only over glyphs in the final layout. Two solver invocations with different available candidate-glyph metrics are operating on different inputs even if they happen to converge on the same final glyph set; this rule makes that difference visible in the \texttt{metrics\_hash}. The hash is computed only over glyphs the layout solve actually consulted, not over the entire font catalog, so that font additions (new glyphs unused by the solve) do not invalidate prior layouts. \end{requirement} \section{Forward References} \label{sec:layoutir:forward} \begin{itemize} \item The constraint solver consuming \texttt{ConstrainedLayoutIR} and producing \texttt{ResolvedLayoutIR} is specified in Chapter~\ref{ch:solver}. \item The serialization of layout cache data for cross-session persistence is specified in Chapter~\ref{ch:format}. \item The complete RenderIR specification, the engraving algorithm specifications (beam grouping, slur curvature, casting-off, accidental ordering), the font catalog implementation, and the renderer are delivered as separate specifications layered on top of this core document. \end{itemize} % =========================================================================== \chapter{File Format} \label{ch:format} This chapter specifies the on-disk file format. Chapter~\ref{ch:semops} established that the canonical document state is the deterministic reduction of an operation-envelope set. This chapter specifies how that operation set, along with acceleration structures, profile declarations, and extension declarations, is laid out on disk in a way that is crash-recoverable, atomically updatable, and forward- compatible. The byte-level encoding details (record layouts, varint conventions, exact field bit widths) are deferred to a companion document, the Binary Format specification. This chapter specifies \emph{what} is stored, how the parts relate, and how the format behaves under concurrent edits, crashes, and forward-compatibility scenarios; the Binary Format specifies \emph{how the bits are laid out}. The file format's name is \texttt{musc} and its file extension is \texttt{.musc}. \section{Design Principles} \label{sec:format:principles} \begin{description} \item[Canonical state is the operation set.] The canonical document is the operation-envelope set (Chapter~\ref{ch:semops}). After pruning, the canonical document is a retained base snapshot plus the operation envelopes after that snapshot's causal frontier. Everything else stored in the bundle (snapshots, layout caches, indexes, rendered artifacts) is an acceleration structure and \MUST{} be discardable. \item[Fixed prelude, atomic superblock commit.] The file begins with a fixed-size header followed by two fixed-size superblock slots. The superblocks are the only mutable on-disk objects in the bundle. Commits append new chunks, write a new manifest chunk, then flip the active superblock by writing to the currently-inactive slot. This makes commits atomic with respect to crashes. \item[Content-addressed immutable chunks.] Every other on-disk object is content-addressed by BLAKE3 of its uncompressed payload (with domain-separated preimage). Chunks are immutable once written. Duplicate content shares storage automatically. \item[Manifest as a table of roots.] The manifest does not carry user-facing data. It carries the operation-set roots, the retained snapshot references, blob references, profile declarations, extension declarations, and the text-projection root. User-facing metadata (title, composer, etc.) lives in operation envelopes or in materialized state, not in the physical header. \item[Crash recovery is by superblock selection.] Recovery is simply: read both superblocks, validate, and select the highest valid generation. Anything not reachable from the selected superblock is unreachable garbage and can be reclaimed by a later conservative GC pass. \item[Forward compatibility through opaque preservation and edit barriers.] Unknown extension chunks are preserved across reads and writes. Unknown required extensions either prevent open or force read-only mode. Unknown optional extensions are preserved unless an edit crosses a declared edit barrier. \item[Streaming reads.] The bundle is designed so that opening a large score touches the prelude, the active superblock, the manifest, and a small set of bootstrap chunks (the most recent canonical-base snapshot plus the envelope index). Per-region content loads on demand. \item[Deterministic text projection of semantic content.] The format admits a deterministic projection to a canonical s-expression form for diff tooling and version control. The text projection preserves canonical document semantics (operation envelopes, profile declarations, extension declarations, reduced state). It does \emph{not} preserve chunk offsets, compression choices, cache chunks, garbage regions, or superblock generation numbers. \item[No encryption, no DRM.] The format \MUSTNOT{} include encryption, access control, or rights-management features. Privacy at rest is the responsibility of the filesystem and operating system. The format is open and offers no mechanism by which user content can be locked away from its author or from other implementations. \end{description} \begin{nongoal} Digital rights management (DRM), content encryption, hardware-binding, license enforcement, and any mechanism that could lock user content to a particular vendor or platform are explicitly out of scope. The format is designed for openness, durability, and user ownership of data. Implementations \MUSTNOT{} add proprietary extensions intended to restrict access to or use of user content. \end{nongoal} \section{Durable Writes} \label{sec:format:durable} This chapter refers throughout to ``durable flushes'' of file contents. The definition is platform-abstracted. \begin{description} \item[Durable flush.] The platform operation that makes previously-written bytes durable according to the host filesystem contract: \texttt{fsync} on POSIX-like systems, \texttt{FlushFileBuffers} or \texttt{NtFlushBuffersFileEx} on Windows, equivalent calls on other platforms. A successful durable flush guarantees that bytes written before the call are on the persistent storage medium when the call returns. \end{description} \begin{requirement} Implementations \MUST{} use the platform's durable flush primitive at the points required by the atomic write protocol (Section~\ref{sec:format:commit}). Implementations \MAY NOT{} rely on filesystem ordering guarantees or write-back caching alone; durability \MUST{} be requested explicitly at the commit point. \end{requirement} \section{The Bundle Layout} \label{sec:format:bundle} A \texttt{.musc} bundle is a single file whose first 576 bytes are a fixed prelude: a 64-byte header followed by two 256-byte superblock slots. The remainder of the file holds variable-length content: chunk payloads, the manifest chunk, blob payloads, the text projection (if present), and unreachable garbage from prior commits that has not yet been collected. \begin{lstlisting}[language=Rust] pub struct BundleLayout { /// Fixed 64-byte header at offset 0. pub header: FixedHeader, /// Two fixed-size superblock slots at offsets 64 and 320. pub superblock_a: Superblock, // at offset 64 pub superblock_b: Superblock, // at offset 320 /// Variable-length content begins at offset 576. /// All chunks, the manifest, and blobs live here. pub body: ContentRegion, } \end{lstlisting} \subsection{The Fixed Header} The header is exactly 64 bytes at file offset zero. It identifies the format and locates the superblock slots. The header never changes after the file is created. \begin{lstlisting}[language=Rust] pub struct FixedHeader { /// Magic bytes: ASCII "MUSCBND\0" (8 bytes). pub magic: [u8; 8], /// Major format version. Major version changes are /// non-backward-compatible. pub format_major: u16, /// Minor format version. Minor version changes are /// backward-compatible. pub format_minor: u16, /// Length of this header in bytes (currently always 64). /// Reserved for future expansion via header_length growth. pub header_length: u32, /// Offset of superblock slot A. Currently always 64. pub superblock_a_offset: u64, /// Offset of superblock slot B. Currently always 320. pub superblock_b_offset: u64, /// 128-bit file UUID, set at creation of the physical bundle. /// Changes on Save As (the physical bundle is a new file). /// Distinct from document_id, which identifies the logical work. pub file_uuid: FileUuid, /// Reserved bytes at offsets 48..60. MUST be zero on write; /// MUST be ignored on read. Future minor versions MAY use /// these bytes for additional fields whose absence is /// interpreted as zero. pub reserved: [u8; 12], /// CRC-32C of bytes 0..60 of this header (i.e., all preceding /// fields plus the reserved bytes). At fixed offset 60..64. pub header_crc: u32, } \end{lstlisting} \begin{requirement} The header \MUST{} be exactly 64 bytes at file offset zero. Its contents \MUSTNOT{} change after file creation, except for the case where a major-version upgrade rewrites the entire file. Readers \MUST{} verify the magic bytes and the header CRC before consulting any other part of the file. Reserved bytes \MUST{} be zero when written by current implementations. Readers \MUST{} ignore the values of reserved bytes (future versions may use them). The \texttt{file\_uuid} is fixed for the lifetime of a physical bundle and persists across commits to that bundle. A filesystem-level byte copy preserves the \texttt{file\_uuid} (the copy is the same physical bundle duplicated). Save As, export-as-new-bundle, and derivative-work creation \MUST{} assign a fresh \texttt{file\_uuid}; the resulting bundle is a new physical bundle, distinct from its source. The \texttt{file\_uuid} identifies the physical bundle, not the logical work. Two bundles with the same \texttt{file\_uuid} are byte-level copies of the same physical bundle; two bundles with the same \texttt{document\_id} (defined in the manifest; see Section~\ref{sec:format:identity}) are versions of the same logical work, which may or may not share their \texttt{file\_uuid}. \end{requirement} \subsection{The Superblock Slots} Each superblock slot is exactly 256 bytes. The bundle has two slots (A at offset 64, B at offset 320). One is active; the other is the write target for the next commit. Atomic commit is the act of writing the inactive slot and durably flushing it. \begin{lstlisting}[language=Rust] pub struct Superblock { /// Magic bytes: ASCII "MUSCSUPR" (8 bytes). pub magic: [u8; 8], /// Generation counter. The active superblock is the one with /// the highest valid generation. pub generation: u64, /// Offset of the manifest chunk for this generation. pub manifest_offset: u64, /// Length of the manifest chunk in bytes. pub manifest_length: u64, /// BLAKE3 content hash of the manifest chunk's uncompressed /// payload, with domain-separated preimage /// (Section~\ref{sec:format:hashing}). pub manifest_hash: ContentHash, /// Schema version of the manifest at this generation. pub manifest_schema_version: SchemaVersion, /// Reduction algorithm version that produced any /// canonical-base snapshots in this manifest. pub reduction_algorithm_version: ReductionAlgorithmVersion, /// Profile under which this superblock is valid. pub profile_id: ProfileId, /// Commit state. Distinguishes a fully-committed superblock /// from one that was written but whose commit had not yet /// completed when the writer crashed (rare; the protocol does /// not normally leave such a state visible). pub commit_state: CommitState, /// Wall-clock timestamp at commit, in canonical units. /// Advisory only; superblock selection is by generation, not /// by timestamp. pub commit_timestamp: WallClockTime, /// CRC-32C of the bytes of this superblock up to (but not /// including) the CRC field. Lets readers reject torn writes. pub superblock_crc: u32, /// Reserved padding to 256 bytes. MUST be zero on write; /// MUST be ignored on read. pub reserved: [u8; /* computed */ 256 - HEADER_FIELDS_SIZE], } pub enum CommitState { /// Fully committed. The manifest at manifest_offset is the /// canonical state for this generation. Committed, /// Reserved for future use; writers MUST NOT produce /// non-Committed values in this version of the format. Reserved(u32), } \end{lstlisting} \subsection{Superblock Selection} \begin{requirement} On open, readers \MUST{} perform the following selection: \begin{enumerate} \item Read both superblock slots. \item For each slot, validate: magic bytes, CRC, that the referenced manifest chunk's actual BLAKE3 hash matches the declared \texttt{manifest\_hash}, and that \texttt{commit\_state} is \texttt{Committed}. A slot failing any of these checks is \emph{not valid} for ordinary selection. \item A slot whose \texttt{commit\_state} is not \texttt{Committed} is invalid for ordinary selection but \MAY{} be inspected in diagnostic recovery mode. Writers in this format version \MUSTNOT{} produce such states under normal commit; observing one indicates either a crashed writer (recovery falls back to the other slot per the atomic-write protocol) or a non-conforming writer. \item If both slots are valid and their generations differ by more than one, the reader \MUST{} report an integrity anomaly (the file may have been tampered with, accidentally merged, or written by a non-conforming implementation). The reader \MAY{} continue in read-only recovery mode using the highest-generation valid slot; it \MUSTNOT{} silently treat the file as normal. \item If both slots are valid and their generations differ by exactly one, the slot with the higher generation is active. \item If both slots are valid and their generations are \emph{equal}, the reader \MUST{} compare the slots' \emph{load-bearing} selection fields: \texttt{manifest\_hash}, \texttt{manifest\_schema\_version}, \texttt{reduction\_algorithm\_version}, and \texttt{profile\_id}. The advisory fields \texttt{commit\_timestamp} and the physical chunk offset/length are \emph{excluded} from this comparison. \begin{itemize} \item If every load-bearing field is equal, the two slots select equivalent canonical state; the reader \MUST{} deterministically choose slot~A and open normally. \item If any load-bearing field differs, the reader \MUST{} report a format-layer \texttt{IntegrityAnomaly} of kind \texttt{DivergentSameGeneration} and open the bundle read-only. Two genuinely different committed states cannot share a generation under a conforming writer (the atomic-write protocol increments the generation on every commit), so the divergence is a structural failure, not a state the reader may silently resolve by picking one. \end{itemize} \item If exactly one slot is valid, that slot is active. \item If neither slot is valid, the file is corrupt; readers \MUST{} surface this as a hard error and \MUSTNOT{} synthesize state. \end{enumerate} \end{requirement} \begin{rationale} The advisory fields are excluded from the equal-generation comparison because they do not determine which canonical state a slot selects: \texttt{commit\_timestamp} is wall-clock metadata, and the physical offset/length merely locate the manifest chunk in the file. Two slots that agree on the four load-bearing fields name the same canonical document even if they were written at different times or to different offsets, so treating an advisory-only difference as divergence would raise false anomalies on benign repacks. Conversely, a difference in any load-bearing field means the two slots disagree about canonical state itself, which is the precise condition that must not be silently resolved. \end{rationale} \section{The Manifest} \label{sec:format:manifest} The manifest is a table of roots and declarations. It is itself a chunk (content-addressed, immutable). Each commit writes a new manifest chunk and points the inactive superblock slot at it. \begin{lstlisting}[language=Rust] pub struct Manifest { /// Identifier of the logical work. Stable across Save As /// operations and across copies that are intended to remain /// versions of the same work. May persist across forks if /// the user explicitly preserves it. pub document_id: DocumentId, /// Optional identifier of a shared ancestor document. Used by /// version-control workflows and fork-tracking to express /// genealogy. Absent for documents with no declared ancestry. pub lineage_id: Option, /// Identifier of this manifest. Each commit produces a new /// ManifestId; old manifests are retained in the chunk store /// until garbage collection removes them subject to the /// retention policy. pub manifest_id: ManifestId, /// Generation counter matching the superblock that references /// this manifest. pub generation: u64, /// Operation-envelope blocks defining the canonical document. /// Block ordering is irrelevant for canonical state (envelopes /// are a set), and the canonical manifest encoding sorts chunk /// references ascending by their encoded form (Appendix D), so /// a writer cannot order this list by stamp; stamp-ordered /// scanning goes through operation_block_summaries below. pub operation_roots: Vec, /// Per-block summary metadata (Section on operation-envelope /// blocks below), keyed by the block's ChunkId in canonical /// ascending order. Supplied by the operation layer and carried /// opaquely by the storage layer; non-canonical and optional (a /// block need not have an entry). Lets a reader select or skip /// a block by causal frontier or stamp range without decoding /// its envelopes. pub operation_block_summaries: BTreeMap, /// Optional operation index chunk: maps OperationId to /// (block, offset) for fast random access. If absent, /// implementations rebuild on demand by scanning blocks. pub operation_index_root: Option, /// The active canonical-base snapshot, if pruning has occurred. /// At most one canonical base is active at a time. When /// present, the canonical document is the base snapshot's /// materialized state plus the deterministic reduction of /// envelopes whose OperationIds are not covered by the base's /// causal frontier. pub canonical_base: Option, /// Acceleration snapshots: caches that materialize state at /// various causal frontiers for fast incremental loading. /// These MAY be discarded freely without affecting canonical /// state. pub acceleration_snapshots: Vec, /// Blob references for large opaque content (audio, images, /// custom fonts). pub blob_roots: Vec, /// Profile declarations: which conformance profiles this /// manifest claims to satisfy. pub profile_declarations: Vec, /// Extension declarations: which extensions contributed to /// this manifest, with their required/optional flag and /// edit barriers. pub extension_declarations: Vec, /// Text projection root chunk, if a text projection is /// maintained in this bundle. Non-canonical accelerator. pub text_projection_root: Option, /// Optional integrity index root chunk: cross-references chunk /// hashes for faster integrity verification of large bundles. /// Non-canonical accelerator; absence is permitted. pub integrity_root: Option, } \end{lstlisting} \subsection{Canonical and Non-Canonical Manifest Roots} \label{sec:format:roots} The manifest's fields partition cleanly into two groups by their relation to canonical state. \textbf{Canonical roots} (define the document): \begin{itemize} \item \texttt{operation\_roots}: operation-envelope blocks containing the canonical operation set. \item \texttt{canonical\_base}: the active canonical base snapshot, if pruning has occurred. \item \texttt{blob\_roots}: blobs referenced by canonical operations or by canonical reduced state are themselves canonical; blobs referenced only by acceleration structures are non-canonical. \item \texttt{document\_id}, \texttt{lineage\_id}, \texttt{profile\_declarations}, \texttt{extension\_declarations}: structural metadata governing how the canonical document is interpreted. \end{itemize} \textbf{Non-canonical reachable roots} (accelerators only): \begin{itemize} \item \texttt{operation\_index\_root}: rebuildable from operation envelope blocks. \item \texttt{acceleration\_snapshots}: caches at causal frontiers other than the canonical base. \item \texttt{text\_projection\_root}: derivable from canonical state. \item \texttt{integrity\_root}: rebuildable from chunk hashes. \end{itemize} \begin{requirement} The manifest \MUST{} list every chunk that contributes to the current canonical state, transitively through its canonical roots (operation roots, canonical base, canonical blobs and extension chunks). It \MAY{} additionally list non-canonical reachable roots (acceleration snapshots, operation index, text projection, integrity index, layout caches). Chunks not reachable from any manifest root, canonical or non-canonical, are unreachable; readers \MUSTNOT{} rely on them and conservative garbage collection \MAY{} reclaim them (Section~\ref{sec:format:gc}). Non-canonical reachable chunks \MAY{} be rebuilt or replaced without altering canonical state. If a non-canonical chunk's hash fails verification, it \MUST{} be discarded and (if needed) rebuilt; failed verification of a non-canonical chunk is \emph{not} a corruption of the bundle. Failed verification of a canonical chunk (operation envelope block, canonical base snapshot, canonical blob, manifest) \emph{is} corruption and \MUST{} be surfaced as a hard error. User-facing metadata (title, composer, lyricist, copyright, and similar fields) is \emph{not} part of the manifest. Such metadata lives in the operation set and the materialized state. The manifest carries only the structural roots and declarations that the format needs to locate and validate content. \end{requirement} \subsection{File, Document, and Lineage Identity} \label{sec:format:identity} The format distinguishes three identifiers, each serving a different identity question: \begin{description} \item[\texttt{file\_uuid}.] Physical bundle identity. Set at file creation; persists for the lifetime of the physical file. \textbf{Changes on Save As}: a copy created by Save As is a new physical bundle and \MUST{} be assigned a fresh \texttt{file\_uuid}. Used by tools that need to identify ``this exact file on disk'' (caches, backup software, file fingerprinting). \item[\texttt{document\_id}.] Logical work identity. Stable across Save As copies that are intended to remain the same work (the canonical case: ``I'm saving my piece to a backup folder, and it's still the same piece''). \textbf{Persists across forks at the user's discretion}: a fork that represents a derivative work \SHOULD{} have a new \texttt{document\_id}, while a fork that is a working copy of the original \SHOULD{} preserve it. The format does not enforce a policy; the editor surfaces the choice. \item[\texttt{lineage\_id}.] Optional shared ancestor identity. Records that two documents share a common ancestor for version-control or genealogy purposes. Multiple documents \MAY{} share a \texttt{lineage\_id}; the field is set when a fork explicitly preserves ancestry information. \end{description} \begin{requirement} Implementations \MUST{} assign a fresh \texttt{file\_uuid} on any Save As operation. They \MUSTNOT{} carry the source file's UUID into the new file: doing so would conflate physical and logical identity and break tools that rely on \texttt{file\_uuid} for content fingerprinting. The choice of whether to preserve \texttt{document\_id} across a Save As is exposed to the user: the editor \SHOULD{} default to preserving \texttt{document\_id} (the common case is ``save a copy of the same work to a new file'') and \SHOULD{} offer a clear ``Save As Derivative Work'' or equivalent action that mints a new \texttt{document\_id}. The format \MUSTNOT{} treat documents with the same \texttt{document\_id} as automatically synchronizable: that decision belongs to the collaboration transport (Appendix~\ref{app:deferred}). \end{requirement} \subsection{Manifest Encoding} \label{sec:format:manifest-encoding} The manifest chunk is special: it is the bootstrap entry from the superblock into the chunk graph. A cold reader needs to decode the manifest before it has any information from the chunk store. \begin{requirement} In this format version, the manifest chunk \MUST{} be stored \emph{uncompressed}. Its \texttt{CompressionAlgorithm} on disk is implicitly \texttt{None}; the superblock's \texttt{manifest\_length} field is therefore both the on-disk length and the uncompressed payload length. Implementations \MUST{} reject as malformed any bundle whose manifest payload is not directly parseable as a canonical manifest chunk's uncompressed bytes. This rule eliminates a bootstrap chicken-and-egg problem: a reader can decode the manifest using only information present in the fixed header and the active superblock, without first consulting the manifest to learn how the manifest is compressed. All other chunks (operation envelope blocks, snapshots, extension data, layout caches, blobs, integrity indexes, operation indexes, text projection chunks) \MAY{} be compressed. Future format major versions \MAY{} permit manifest compression, but doing so requires explicit superblock fields (\texttt{manifest\_compression}, \texttt{manifest\_uncompressed\_length}) and is a non-backward-compatible change. \end{requirement} \begin{rationale} The manifest is small (a few kilobytes at most for typical scores), so compression yields negligible space savings. Eliminating the bootstrap dependency is far more valuable than the few hundred bytes of compression headroom. \end{rationale} \begin{requirement} \label{req:format:manifest-id} The \texttt{manifest\_id} field \MUST{} be derived as \[ \texttt{ManifestId} = \mathrm{trunc128}\!\left( \mathrm{BLAKE3}(\texttt{"MUSCMNIF"} \Vert \texttt{document\_id} \Vert \texttt{generation} \Vert \texttt{manifest\_body})\right), \] where \texttt{document\_id} is its 16-byte canonical form, \texttt{generation} is the 8-byte little-endian \texttt{u64}, and \texttt{manifest\_body} is the canonical manifest encoding with the \texttt{manifest\_id} field itself \emph{excluded}. The exclusion is normative: a conforming writer \MUST{} omit (equivalently, zero) the \texttt{manifest\_id} field when computing the preimage, so that the identifier does not reference itself. $\mathrm{trunc128}$ takes the leading 16 bytes of the 32-byte BLAKE3 output. Two conforming writers committing the same manifest body at the same generation of the same document therefore derive identical \texttt{ManifestId}s. \end{requirement} \section{Content Hashing} \label{sec:format:hashing} Epiphany uses BLAKE3 as its single content-hashing algorithm. \begin{requirement} All content hashes in the bundle \MUST{} be BLAKE3-256 outputs (32 bytes). Implementations \MUSTNOT{} use any other algorithm for content addressing; SHA-256, SHA-3, and other algorithms \MUSTNOT{} appear in this version of the format. Future major versions \MAY{} introduce additional algorithms; such changes require a new format major version. \end{requirement} \subsection{Domain-Separated Preimages} A naive content-addressing scheme that hashes only payload bytes is vulnerable to collisions across chunks of different semantic kinds. Epiphany uses domain-separated preimages. \begin{lstlisting}[language=Rust] /// Computes the canonical hash preimage for a chunk's content. /// The chunk's compression algorithm is metadata on the ChunkRef, /// not part of the content identity: identical uncompressed /// payloads produce identical hashes regardless of how they are /// physically compressed in storage. This preserves deduplication /// across compression choices. fn hash_preimage( domain_tag: &[u8; 8], chunk_kind: ChunkKind, schema_version: SchemaVersion, uncompressed_length: u64, uncompressed_payload: &[u8], ) -> Vec { let mut preimage = Vec::new(); preimage.extend_from_slice(domain_tag); preimage.extend_from_slice(&chunk_kind.canonical_bytes()); preimage.extend_from_slice(&schema_version.canonical_bytes()); preimage.extend_from_slice(&uncompressed_length.to_le_bytes()); preimage.extend_from_slice(uncompressed_payload); preimage } \end{lstlisting} \begin{requirement} The content hash of a chunk \MUST{} be BLAKE3 of the canonical preimage above. The domain tag, chunk kind, schema version, and uncompressed length are part of the preimage; the compression algorithm is \emph{not}. Two chunks with identical uncompressed payloads, identical kinds, and identical schema versions \MUST{} produce identical hashes regardless of their compression choices. \end{requirement} \begin{rationale} Including compression algorithm in the hash would cause identical content stored with different compression to have different addresses, defeating deduplication and forcing churn during recompression or repack. Including domain tag, kind, and schema version in the preimage closes a real collision: two semantically different chunks with identical raw bytes are no longer interchangeable. The domain tag is a fixed 8-byte string identifying the bundle format and protecting against cross-format collisions if the same BLAKE3 hash were reused in unrelated contexts. The canonical domain tag for \texttt{.musc} chunks is \texttt{"MUSCCHNK"}; manifest chunks use \texttt{"MUSCMANI"}; blob payloads use \texttt{"MUSCBLOB"}. \end{rationale} \section{Chunks} \label{sec:format:chunks} A chunk is an immutable, content-addressed unit of storage. Chunks are referenced from the manifest by \texttt{ChunkRef}. \begin{lstlisting}[language=Rust] pub struct ChunkRef { /// Content identifier: BLAKE3 of the canonical preimage. pub id: ChunkId, /// Kind of chunk. The reader uses this to dispatch parsing. pub kind: ChunkKind, /// Schema version targeted by this chunk's payload encoding. pub schema_version: SchemaVersion, /// Offset of the chunk's compressed payload in the bundle file. pub offset: u64, /// Length of the on-disk compressed payload. pub compressed_length: u64, /// Length of the uncompressed payload (used to size buffers /// before decompression). pub uncompressed_length: u64, /// Compression algorithm. Part of the ChunkRef, not part of /// the chunk's identity. pub compression: CompressionAlgorithm, /// Restated content hash, redundant with the id field but /// included in the ChunkRef for fast verification without /// fetching the chunk. pub hash: ContentHash, } pub enum ChunkKind { /// A block of operation envelopes. OperationEnvelopeBlock, /// An operation-id-to-block index accelerator. OperationIndex, /// A materialized snapshot of canonical state (full or /// partial). Canonical if referenced as the manifest's /// canonical_base; cache (acceleration) otherwise. Snapshot, /// A large opaque blob (audio, image, font, ML model). Blob, /// Extension-defined data, preserved opaquely by core readers. ExtensionData, /// The text projection's root document. TextProjection, /// Layout cache: derivative from canonical state, always /// discardable. LayoutCache, /// Integrity index: cross-references chunk hashes. IntegrityIndex, /// Manifest chunk. Special: referenced from the superblock, /// not from another chunk. Manifest, } pub enum CompressionAlgorithm { /// No compression. Stored payload bytes equal uncompressed. None, /// Zstandard compression. Implementations MUST support reading. Zstd { level: u8 }, /// Other algorithms reserved for future versions. Reserved(u8), } pub struct ContentHash(pub [u8; 32]); /// Chunk identifier. A newtype around ContentHash: the chunk's /// identifier is identical to its content hash, but the type /// distinction is preserved to make role visible at use sites /// (a ChunkId is what you store in a ChunkRef and what you look /// up; a ContentHash is what you compute by hashing). Both occupy /// the same 32 bytes. pub struct ChunkId(pub ContentHash); \end{lstlisting} \begin{requirement} \label{req:format:chunkkind-discriminants} \texttt{ChunkKind::canonical\_bytes()} is a single byte: the variant's declaration-order discriminant. Because that byte is part of the chunk hash preimage (Section~\ref{sec:format:chunks}, ``Domain-Separated Preimages''), this assignment is normative and stable and \MUSTNOT{} be reordered: \begin{center} \begin{tabular}{r l @{\qquad} r l} \toprule \textbf{Disc.} & \textbf{Variant} & \textbf{Disc.} & \textbf{Variant} \\ \midrule 0 & \texttt{OperationEnvelopeBlock} & 5 & \texttt{TextProjection} \\ 1 & \texttt{OperationIndex} & 6 & \texttt{LayoutCache} \\ 2 & \texttt{Snapshot} & 7 & \texttt{IntegrityIndex} \\ 3 & \texttt{Blob} & 8 & \texttt{Manifest} \\ 4 & \texttt{ExtensionData} & & \\ \bottomrule \end{tabular} \end{center} \texttt{CompressionAlgorithm} encodes as a fixed \emph{two} bytes: a declaration-order discriminant byte followed by a single parameter byte that is always present. \texttt{None} $= 0$ (the parameter byte is present but zero, and ignored on read); \texttt{Zstd\{level\}} $= 1$ with the \texttt{level} in the parameter byte; \texttt{Reserved(u8)} $= 2$ with the reserved value in the parameter byte. The parameter byte is written even for \texttt{None}, so the encoding is fixed-width and a reader always consumes two bytes. Compression is \emph{not} part of a chunk's content identity (it is metadata on the \texttt{ChunkRef}), so these discriminants are stable but do not affect chunk hashes. \texttt{ProfileId}'s discriminants are pinned in Section~\ref{sec:format:profiles} (Requirement~\ref{req:format:profileid-discriminants}). \end{requirement} \begin{requirement} Chunks \MUST{} be immutable. Once a chunk is written and its bytes durably flushed, those bytes \MUSTNOT{} be modified. New state is encoded as new chunks with new identifiers; old chunks become unreachable garbage when their referencing manifests are no longer reachable. Implementations \MUST{} verify chunk hashes on first read of each chunk in a session: decompress the payload, compute the canonical preimage's BLAKE3, and confirm the result matches the declared \texttt{hash}. Subsequent reads \MAY{} skip verification if the chunk has been validated within the current session. A chunk whose hash does not match \MUST{} be treated as corrupt: if it is a cache chunk, discard and rebuild; if it is a canonical chunk (operation block, retained snapshot, or blob referenced from the manifest), surface as hard corruption. Readers \MUST{} verify that decompression of a chunk's payload produces exactly \texttt{uncompressed\_length} bytes. Length mismatch is treated as corruption. \end{requirement} \section{Operation Envelope Blocks} \label{sec:format:opblocks} The canonical document is stored as operation-envelope blocks. Each block is a chunk of kind \texttt{OperationEnvelopeBlock} whose payload is a vector of envelopes. A block is identified by its \texttt{ChunkId}; because chunks are content-addressed, that identifier cannot appear inside the block payload itself. Summary metadata for a block travels in the \emph{manifest} (\texttt{operation\_block\_summaries}), keyed by the block's chunk id: the summary is computed by the operation layer from the block's envelopes, and the storage layer carries it as opaque bytes without interpreting it. \begin{lstlisting}[language=Rust] pub struct OperationEnvelopeBlock { /// Operation envelopes in this block. Order within a block is /// not normative for canonical reduction (envelopes are a set) /// but writers SHOULD preserve authoring order within a block /// for diagnostic purposes. pub envelopes: Vec, } /// Manifest-side summary of one block, keyed by the block's /// ChunkId. Non-canonical and optional: a reader MUST NOT rely /// on a summary being present, and canonical state never depends /// on one. pub struct OperationBlockSummary { /// DVV summary covering the envelopes in this block. Enables /// readers to determine causal coverage without parsing every /// envelope. pub dvv_summary: CausalContext, /// Earliest stamp in the block. pub min_stamp: OperationStamp, /// Latest stamp in the block. pub max_stamp: OperationStamp, } \end{lstlisting} \begin{requirement} Writers \SHOULD{} begin a new operation-envelope block when adding another envelope would cause the uncompressed block payload to exceed 1 \,MiB, except when an individual envelope's encoded size exceeds 1\,MiB, in which case the envelope occupies its own block. Readers \MUST{} accept valid blocks of any size up to a profile- declared maximum (default: 64\,MiB uncompressed). Profile declarations \MAY{} raise or lower this bound. Blocks exceeding the active profile's bound \MUST{} be treated as malformed. The \texttt{operation\_roots} field of the manifest \MUST{} list every operation-envelope block that contributes to the canonical document. The set of envelopes \emph{is} the union of all envelopes across all referenced blocks; block boundaries are storage artifacts, not semantic structure. \end{requirement} \subsection{The Operation Index} The optional \texttt{operation\_index\_root} chunk maps each \texttt{OperationId} to the \texttt{ChunkRef} of its enclosing block plus an offset within the block. It enables $O(\log n)$ lookup of an operation by id. \begin{requirement} The operation index is an acceleration structure, not canonical. If absent, readers rebuild it by scanning all blocks. If present but corrupt or stale, readers \MUST{} reject the index and rebuild from blocks. Writers \SHOULD{} rebuild or incrementally update the operation index at commit time when the operation set has grown significantly since the last commit. \end{requirement} \section{Snapshots and Canonical Bases} \label{sec:format:snapshots} A snapshot is a materialized score state at a particular causal frontier. Snapshots have two distinct roles: \begin{description} \item[Acceleration snapshot.] A cache of the materialized state at some point, used to skip reduction work. Listed in the manifest (or not), but \emph{not} declared as a canonical base. Discardable. \item[Canonical base.] A materialized state at a causal frontier that the manifest declares as the base after pruning. Together with the operation envelopes after the frontier, this constitutes the canonical document. Not discardable while it remains declared. \end{description} \begin{lstlisting}[language=Rust] pub struct SnapshotRef { pub snapshot_id: SnapshotId, /// The causal frontier this snapshot materializes: the /// envelopes whose effects are baked into the snapshot. pub covers_causal_frontier: CausalContext, /// Reduction algorithm version under which the snapshot was /// produced. Snapshots produced under an earlier algorithm /// version cannot be used as canonical bases under a later /// algorithm without rebuilding. pub reduction_algorithm_version: ReductionAlgorithmVersion, /// Profile under which the snapshot was produced. pub profile_id: ProfileId, /// Root chunk of the snapshot's materialized state. pub root: ChunkRef, /// Hash of the snapshot's root chunk for fast verification. pub hash: ContentHash, } \end{lstlisting} \subsection{The Canonical Document Identity} \begin{requirement} The canonical document is defined as follows: \begin{itemize} \item If \texttt{canonical\_base} is \texttt{None}, the canonical document is the deterministic reduction (Section~\ref{sec:semops:reduction}) of the union of envelopes in all \texttt{operation\_roots} blocks. \item If \texttt{canonical\_base} is \texttt{Some(base)}, the canonical document is the base snapshot's materialized state plus the deterministic reduction of all envelopes whose \texttt{OperationId} is \emph{not covered} by the base's \texttt{covers\_causal\_frontier}. An \texttt{OperationId} is covered by a \texttt{CausalContext} if and only if it appears in the dotted version vector's per-replica contiguous range or among its dots (i.e., causal-precedes-or-equals under the DVV's induced partial order). HLC stamps \MUSTNOT{} be used to determine coverage; only the DVV frontier is canonical. \item A snapshot may serve as the canonical base only if its \texttt{reduction\_algorithm\_version} equals the active superblock's \texttt{reduction\_algorithm\_version} and its \texttt{profile\_id} matches the manifest's profile declarations. \end{itemize} At most one \texttt{canonical\_base} is active at any time. Pruning replaces the current canonical base with a new one whose frontier strictly dominates the previous frontier under the DVV partial order; the old base becomes unreachable and is subject to garbage collection. Acceleration snapshots (\texttt{acceleration\_snapshots}) \MUST{} be ignored for the purposes of canonical state. They exist to accelerate cold open and incremental reduction; if their content disagrees with what the canonical reduction would produce, they \MUST{} be rebuilt or discarded. Caches, indexes, layout chunks, and other materialized artifacts in the bundle are acceleration structures only and \MUST{} be ignored or rebuilt if they disagree with the canonical document. \end{requirement} \subsection{Pruning} \begin{requirement} Pruning is the act of removing operation envelopes that are causally covered by a new canonical base snapshot. Pruning: \begin{enumerate} \item Materializes a snapshot covering the chosen causal frontier (a DVV). \item Replaces the current \texttt{canonical\_base} field with a \texttt{SnapshotRef} naming the new snapshot. \item Removes operation-envelope blocks whose envelopes are \emph{entirely} covered by the new frontier. Blocks with mixed coverage \MUST{} be split or retained whole; uncovered envelopes \MUST{} remain reachable. \item Commits the new manifest via the atomic write protocol (Section~\ref{sec:format:commit}). \end{enumerate} Pruning \MUSTNOT{} alter the canonical document state: the reduction of (base snapshot $+$ post-frontier envelopes) under the active algorithm \MUST{} equal the reduction of the pre-pruning envelope set. Implementations \MUSTNOT{} prune envelopes outside the explicit pruning protocol. Operation envelopes are not silently garbage- collected based on age or other heuristics. \end{requirement} \section{Blobs} \label{sec:format:blobs} The blob store holds large opaque content: audio reference tracks, embedded images, custom fonts, ML model checkpoints. \begin{requirement} \label{req:format:blob-hash-shape} A \texttt{BlobId} \MUST{} be BLAKE3 over the \emph{bare} preimage \texttt{"MUSCBLOB"} $\Vert$ \texttt{uncompressed\_payload}: the 8-byte domain tag immediately followed by the uncompressed payload bytes, with \emph{no} chunk-kind, schema-version, or length fields. This is deliberately \emph{unlike} the structured chunk preimage of Section~\ref{sec:format:chunks} (which commits to kind, schema version, and length): a blob is opaque payload with no schema, so its identity commits only to the domain tag and the bytes. As with chunks, the compression algorithm is metadata on the \texttt{BlobRef} and is \emph{not} part of the blob's identity, so identical payloads stored under different compression share a \texttt{BlobId}. \end{requirement} \begin{lstlisting}[language=Rust] pub struct BlobRef { pub blob_id: BlobId, pub media_type: String, // RFC 6838 media type pub offset: u64, pub compressed_length: u64, pub uncompressed_length: u64, pub compression: CompressionAlgorithm, pub hash: ContentHash, /// Optional declared maximum size that readers MAY use to /// reject blobs that exceed reader resource limits. pub declared_max_uncompressed_length: Option, } \end{lstlisting} \begin{requirement} Readers \MUST{} apply resource limits to blob loading: uncompressed length \MUST{} be checked against the reader's policy before decompression begins; \texttt{media\_type} \MUST{} be validated against the reader's accepted list before any type-specific parsing. Implementations \MUSTNOT{} eagerly load all blobs at open; blobs \MUST{} be streamed on demand. Operations requiring blob content \MUST{} be designed for lazy access. Blob content is opaque to the core. The core stores, transmits, and integrity-checks blobs; it does not interpret their bytes. \end{requirement} \section{The Atomic Write Protocol} \label{sec:format:commit} A commit transforms the bundle from one canonical state to another. The protocol uses two superblock slots so that a crash at any point during commit leaves the bundle in either the pre-commit state (active superblock unchanged) or the post-commit state (active superblock now points at the new manifest); no intermediate state is visible. \begin{requirement} Commits \MUST{} follow this sequence: \begin{enumerate} \item Write all new chunks (envelope blocks, possibly a new snapshot, possibly a new operation index, etc.) to file regions outside the prelude and outside currently-reachable chunks. Writers \MAY{} append at end-of-file or fill known- unreachable regions, but \MUSTNOT{} overwrite any currently-reachable chunk. \item Durably flush the file. \item Compute the new manifest chunk's payload and write it. \item Durably flush the file. \item Compute the new \texttt{Superblock} with generation $= \text{active\_generation} + 1$ and \texttt{manifest\_offset} pointing at the new manifest chunk. \item Write the new superblock to the currently-inactive superblock slot (a 256-byte write at a fixed offset). \item Durably flush the file. \emph{This is the commit point.} Before this flush returns successfully, the active superblock is the old one; after it returns successfully, the active superblock is the new one. \end{enumerate} After step 7, the previously-active superblock slot becomes the inactive slot for the next commit. Garbage collection is a separate, optional, deferred operation (Section~\ref{sec:format:gc}). \end{requirement} \subsection{Crash Recovery} \begin{requirement} On open after any crash, recovery follows the superblock selection rule (Section~\ref{sec:format:bundle}). The bundle is always in a recoverable state: \begin{itemize} \item Crash between steps 1 and 7: the active superblock is unchanged; the new chunks and the new manifest are unreachable garbage; the document opens at the pre-commit state. A later GC reclaims the orphaned bytes. \item Crash during step 7: the superblock slot write may be torn. The CRC check rejects torn writes. Recovery falls back to the other slot. The document opens at the pre-commit state. The partially-written slot will be overwritten by the next successful commit. \item Crash after step 7: the new superblock is active. The document opens at the post-commit state. No special recovery is needed. \end{itemize} Recovery \MUSTNOT{} synthesize or guess: if neither superblock validates, the file is corrupt and \MUST{} be reported as such. \end{requirement} \section{Garbage Collection and Retention} \label{sec:format:gc} Garbage collection reclaims bytes occupied by chunks that are no longer reachable from any retained manifest. Retention policy governs which old manifests are preserved for rollback. \subsection{Retention Policy} \begin{lstlisting}[language=Rust] pub struct RetentionPolicy { /// Maximum number of previous manifests to retain beyond the /// active one. 0 means "no rollback retention"; the active /// manifest is the only one preserved. The default declared by /// the Full profile is profile-defined. pub retain_previous_manifests: u32, /// Optional wall-clock duration beyond which old retained /// manifests may be reclaimed regardless of count. None means /// no time-based eviction. pub retain_duration: Option, /// Whether to preserve manifests explicitly marked as named /// checkpoints (user-invoked "save a version" action) beyond /// the count and duration limits. pub retain_named_checkpoints: bool, } \end{lstlisting} \begin{requirement} The active conformance profile \MUST{} declare a \texttt{RetentionPolicy}. Implementations \MUST{} honor the declared policy: chunks reachable from any retained manifest \MUSTNOT{} be garbage-collected, and retention selection (which manifests are retained) \MUST{} be deterministic given the manifest history and the policy. Rollback is the act of writing a new superblock that points at a previously-active manifest still reachable in the chunk store. The fixed layout has exactly two superblock slots; rollback does not require additional on-disk superblocks. It requires only that the target manifest's reachable chunks remain in the bundle, which the retention policy guarantees. \end{requirement} \subsection{Garbage Collection Rules} \begin{requirement} Garbage collection is a conservative, optional, deferred operation. It \MUST{} preserve every byte reachable transitively from: \begin{enumerate} \item The active superblock's manifest. \item Every retained manifest selected by the active \texttt{RetentionPolicy}. \item Any pending edit's working set (chunks written but not yet committed). \end{enumerate} Garbage collection \MUSTNOT{} run as part of a commit's critical path. It \MAY{} run as a background operation or as an explicit user-invoked compaction. Pre-GC and post-GC bundles \MUST{} contain identical canonical state and identical retained-rollback reachability. \end{requirement} \section{Forward Compatibility and Edit Barriers} \label{sec:format:fwdcompat} The bundle must round-trip cleanly across implementations that may not all understand every extension referenced in it. \subsection{Extension Declarations} \begin{lstlisting}[language=Rust] pub struct ExtensionDeclaration { pub extension_id: ExtensionId, pub version: SemVer, /// If true, an implementation that does not understand this /// extension MUST refuse to open the bundle for editing, but /// MAY open read-only. pub required: bool, /// Root chunks owned by this extension. Opaque to core /// readers; preserved across reads and writes. pub preserved_chunk_roots: Vec, /// Object kinds this extension contributes to or depends on, /// for edit-barrier evaluation. pub affected_object_kinds: Vec, /// Edit barriers declared by this extension. pub edit_barriers: Vec, } pub struct EditBarrier { /// Scope of the barrier: whole score, region, staff instance, /// analysis layer, object set, pitch space, tuning context, etc. pub scope: BarrierScope, /// Object kinds protected by this barrier. pub affected_object_kinds: Vec, /// Operation kind tags prohibited by this barrier. A barrier /// prohibits an operation class, not one exact payload, so it /// references OperationKindTag rather than OperationKind. pub prohibited_operation_kinds: Vec, /// Additional condition narrowing the barrier (e.g., applies /// only when a specific attachment is present). pub condition: BarrierCondition, } /// A discriminator-only view of OperationKind (Chapter ch:semops), /// without payload. Used by edit barriers, conformance reports, /// and diagnostic surfaces where the operation's class matters /// but its payload does not. There is a canonical mapping from /// OperationKind to OperationKindTag. pub enum OperationKindTag { InsertEvent, DeleteEvent, ModifyEvent, RespellPitch, Transpose, CreateCrossCutting, DeleteCrossCutting, ModifyCrossCutting, ChangeRegionTimeModel, InsertRegion, DeleteRegion, InsertStaffInstance, DeleteStaffInstance, SetUserSystemBreak, SetUserPageBreak, DeclareTransaction, Registered(OperationKindRegistryId), // Appended after Registered (append-only vocabulary; the // tag's wire discriminants never reorder). Phase-2/Phase-3 // operation-catalog growth: InsertIdentifiedPitch, DeleteIdentifiedPitch, ModifyIdentifiedPitch, CreateVoice, DeleteVoice, SetMetadata, SetMetricGrid, InsertStaff, SetTimeSignature, SetTempoSegment, SetStaffLayout, // Schema-major-2 revision (repeat authoring). The pair keeps // its kind names verbatim, as the cross-cutting tags do. CreateRepeatStructure, DeleteRepeatStructure, } pub enum BarrierScope { WholeScore, Region(RegionId), StaffInstance(StaffInstanceId), AnalysisLayer(AnalysisLayerId), ObjectSet(Vec), PitchSpace(PitchSpaceId), TuningContext, /// Registered scope kind for grammar-specific scoping. Registered(BarrierScopeRegistryId), } /// Additional narrowing condition for an edit barrier. The barrier /// applies only when its scope, affected object kinds, prohibited /// operation kinds, and this condition all match the candidate edit. pub enum BarrierCondition { /// The barrier applies unconditionally within its scope. Always, /// The barrier applies only while the named object exists /// (not tombstoned). Used when an extension's invariant /// depends on a specific object remaining live. ObjectExists(TypedObjectId), /// The barrier applies only when the named object carries /// data declared by the named extension. Used when the /// invariant protected by the barrier exists only for /// objects participating in the extension. ObjectHasExtensionData { object: TypedObjectId, extension: ExtensionId, }, /// Conjunction: all conditions must match. All(Vec), /// Disjunction: any condition matching is sufficient. Any(Vec), /// Negation: the inner condition must not match. Not(Box), /// Registered condition kind, evaluated by an extension-aware /// implementation. Core implementations evaluate Registered /// conditions as Always (conservative): they treat the barrier /// as active whenever an unknown extension's condition would /// otherwise narrow it. Registered(BarrierConditionRegistryId), } \end{lstlisting} \subsection{Behavior Under Unknown Extensions} \begin{requirement} When an implementation opens a bundle declaring an extension it does not understand: \begin{itemize} \item If the extension is \texttt{required = true}, the implementation \MUST{} either refuse to open the bundle or open it strictly read-only. It \MUSTNOT{} edit. \item If the extension is \texttt{required = false}, the implementation \MAY{} open the bundle for editing, but \MUST{} preserve the extension's \texttt{preserved\_chunk\_roots} across reads and writes. The chunks are treated as opaque bytes. \item Edits \MUST{} be checked against every active edit barrier. An edit matching a barrier's scope, affected object kinds, and operation kinds is prohibited unless the user explicitly performs an \emph{unsafe edit}. \item An unsafe edit is an explicit user action that acknowledges loss of extension data. The unsafe-edit operation \MUST{} tombstone the relevant extension chunks (so they are no longer preserved) rather than silently breaking extension invariants. \end{itemize} The unsafe-edit mechanism prevents unknown extensions from rendering a document permanently unedittable while still preserving extension data by default. \end{requirement} \begin{requirement} \label{req:format:barrier-matching} \textbf{Barrier matching for target-free and opaque operations (ratified Pass 12).} An operation with no graph target (\texttt{SetMetadata}, \texttt{DeclareTransaction}) is matched by \emph{score-wide} barriers only (\texttt{BarrierScope::WholeScore}); a barrier with any narrower scope cannot match it, because there is no target to test the scope against. An opaque \texttt{Registered} operation is matched \emph{fully conservatively}: since its targets and effects are unknown to a core implementation, it matches every active barrier whose remaining predicates do not exclude it. \end{requirement} \begin{requirement} \label{req:format:unsafe-tombstone} \textbf{Unsafe-edit tombstone semantics (ratified Pass 12).} Crossing a barrier by an unsafe edit immediately deactivates the owning extension's \emph{remaining} barriers for the editing session --- the extension's invariants are already forfeit, so its other barriers no longer protect anything. The crossing \MUST{} be durably recorded at the next bundle commit: the pending tombstone set survives the session that performed the unsafe edit. A tombstoned extension that was declared \texttt{required = true} leaves the bundle openable read-only by implementations that depended on that extension (its invariants can no longer be trusted); implementations that never understood it proceed under the unknown-extension rules above. The manifest-side \emph{byte encoding} of the tombstone record is deferred to the Binary Format companion (the manifest schema is frozen at major~0, so the record must ride an append-safe channel); see the companion's open questions. \end{requirement} \section{Text Projection} \label{sec:format:textproj} The format admits a deterministic projection to a canonical s-expression text form. The text projection is normative. \begin{requirement} The text projection \MUST{} preserve, deterministically and bidirectionally with the binary form: \begin{itemize} \item All operation envelopes (with their identities, stamps, causal contexts, transaction groupings, and payloads). \item All profile declarations. \item All extension declarations, including their preserved chunk roots, with extension payloads encoded as base64 or another canonical text form. \item The active canonical base snapshot's reference, frontier, and reduction algorithm version (the snapshot payload itself \MAY{} be encoded compactly or referenced externally). \item All canonical reduced state. \end{itemize} The text projection \MUSTNOT{} be required to preserve: \begin{itemize} \item Chunk offsets within the file. \item Compression algorithm choices. \item Cache chunks (operation indexes, layout caches, integrity indexes). \item Garbage bytes from prior commits. \item Superblock generation numbers, slot assignments, or CRCs. \end{itemize} Two binary bundles whose canonical document semantics are identical \MUST{} project to identical text. Two text projections that parse to identical canonical document semantics \MUST{} re-serialize to binary bundles whose canonical document semantics are identical (though their physical layout, chunking, and compression \MAY{} differ). \end{requirement} \subsection{Use Cases} The text projection serves three primary use cases: \begin{description} \item[Version control.] Text projections diff and merge in line-based tools. Merge conflicts surface at the operation- envelope level, which is the meaningful level for collaborative editing. \item[Format inspection and debugging.] A canonical text form enables direct inspection of bundle contents without binary tooling. \item[Long-term archival.] Text formats survive transitions between binary specifications. The text projection's structure is stable across binary format major versions where the semantic content is preserved. \end{description} \section{Schema Versioning} \label{sec:format:schema} Chunk payloads are encoded against a schema declared by each chunk's \texttt{schema\_version} field. Schemas evolve under a semantic versioning discipline. \begin{lstlisting}[language=Rust] pub struct SchemaVersion { pub major: u16, pub minor: u16, } \end{lstlisting} \begin{requirement} Schema versioning rules: \begin{itemize} \item Major version changes are non-backward-compatible. Readers that do not support the chunk's major schema version \MUST{} handle it according to the chunk's role: \begin{itemize} \item \emph{Canonical chunks} (operation envelope blocks, the canonical base snapshot, the manifest, and blobs referenced by canonical operations or reduced state) \MUST{} be either parseable or the bundle \MUST{} be opened in read-only preservation mode (or refused). A reader that cannot parse a canonical chunk cannot know the canonical document. \item \emph{Non-canonical chunks} (acceleration snapshots, operation index, layout caches, text projection, integrity index, extension-defined chunks declared as preservable opaque content) \MAY{} be treated as opaque preserved content; the reader does not need to understand them to open the bundle, and writes preserve them verbatim. \end{itemize} \item Unknown canonical operation payloads (operation kinds not in the reader's understood catalog) require the relevant extension support or the reader \MUST{} open the bundle read-only without materializing the unknown operations. Materializing an unknown operation \MUSTNOT{} proceed silently: doing so would fork canonical state against any reader that does understand the operation. \item Minor version changes are backward-compatible. A reader supporting major version $N$ \MUST{} accept any minor version $N.\textit{m}$ for $m \leq$ the reader's supported minor. Under the positional, frozen-layout wire form, a minor change \MUST{} only \emph{append discriminants to the append-safe vocabularies the Binary Format companion defines} (its open \texttt{Registered} vocabularies, plus ratified appends to \texttt{OperationKind}, \texttt{OperationKindTag}, \texttt{OperationPayload}, and the closed value-layer unions), which leaves the bytes of every prior value untouched; it \MUSTNOT{} add or reorder a struct field --- adding a field, even an \texttt{Option}, occupies a new positional slot and shifts subsequent bytes, so it is a \emph{major} change (Binary Format companion, \sectionsc{What ``Additive'' Means Here}). \item The manifest's \texttt{manifest\_schema\_version} declares the schema of the manifest itself. Each chunk's \texttt{schema\_version} field declares the schema of that chunk's payload. \item Schema evolution is governed by the Binary Format companion specification, which defines the wire encoding for each schema version. \end{itemize} \end{requirement} Schema major~1 is the first data-model expansion major: it adds \texttt{Canvas.layout\_defaults}, \texttt{Instrument.range}, and \texttt{Region.permits\_spanning\_slurs} to the graph, and unifies the non-canonical resolved-layout length-prefix width. Its wire form and the byte-for-byte migration from major~0 are defined in the Binary Format companion. The canonical-base \texttt{MaterializedState} embeds none of these values, so it is byte-identical across the bump and stays major~0. \texttt{Canvas.layout\_defaults} and \texttt{Instrument.range} reach only the non-canonical acceleration (full-\texttt{Score}) snapshot; but \texttt{Region.permits\_spanning\_slurs} also reaches the \emph{canonical} operation layer, because \texttt{CreateRegion} embeds a full \texttt{Region}, so an operation-envelope block bearing a v1 \texttt{CreateRegion} is major~1. Consequently a major-0-only reader opens a major-1 bundle \emph{fully} only when the bundle carries no v1 \texttt{CreateRegion} operation; otherwise it reads the major-0 canonical base and manifest but opens read-only (it cannot replay the v1 canonical operations), per the canonical/non-canonical rules above. It always discards the higher-major non-canonical caches. A major-1 reader migrates a major-0 acceleration snapshot and a major-0 \texttt{CreateRegion} payload on read (default-filling the new fields). Schema major~2 is the second data-model expansion major: it fills the truncated bodies of the cross-cutting structures (\texttt{Slur}, \texttt{Tie}, \texttt{Beam}, \texttt{Spanner}), \texttt{RepeatStructure} (kind and voltas), \texttt{Staff} and \texttt{StaffLineConfiguration}, \texttt{Instrument}, and \texttt{ScoreMetadata}, per this revision's Chapter~5 definitions. The same per-payload-type rules apply: the canonical-base \texttt{MaterializedState} embeds none of these values and stays major~0; the filled values reach the acceleration snapshot \emph{and}, through the eight operation payloads that embed them (\texttt{CreateCrossCutting}, \texttt{ModifyCrossCutting}, \texttt{InsertStaff} and \texttt{InsertStaffInstance} (the Operation Catalog's \texttt{CreateStaff} / \texttt{CreateStaffInstance}), \texttt{SetStaffLayout}, \texttt{SetMetadata}, \texttt{CreateRepeatStructure} (the repeat-authoring revision --- it embeds the filled \texttt{RepeatStructure} unconditionally, so it is \emph{born at v2} and every block carrying one stamps major~2; its \texttt{DeleteRepeatStructure} sibling carries a bare identifier, a major-0 layout, and stamps major~0), and --- transitively, because an embedded \texttt{Region} may carry a \texttt{StaffInstance} whose \texttt{staff\_lines\_override} embeds the filled \texttt{StaffLineConfiguration} --- \texttt{InsertRegion} (\texttt{CreateRegion})), the canonical operation layer. A block is stamped with the \emph{lowest} schema major whose layouts decode its bytes (the companion's minimal-stamping rule --- deterministic, so identical content hashes identically): any v2-dependent bytes make it major~2, and a lower-major-only reader opens such a bundle read-only. The wire form and the total default-filling v1${\to}$v2 migration are defined in the Binary Format companion. \section{Format Profiles} \label{sec:format:profiles} A format profile defines a conformance subset that implementations \MAY{} promise to support. Profiles enable lightweight readers to declare their capabilities precisely. \begin{lstlisting}[language=Rust] pub struct ProfileDeclaration { pub profile_id: ProfileId, pub version: SemVer, /// Constraints imposed by this profile (maximum block size, /// permitted compression algorithms, required extensions, etc.) pub constraints: ProfileConstraints, } pub enum ProfileId { /// Full profile: all features supported, no restrictions. Full, /// Read-only profile: bundles produced under this profile MAY /// be opened by readers that cannot edit. Editing implementations /// SHOULD upgrade the profile on first edit. ReadOnly, /// Lite profile: a reduced feature set for embedded or mobile /// readers. Restricts maximum block sizes, snapshot complexity, /// and permitted extensions. Lite, /// Custom profile defined by a registered profile declaration. Custom(ProfileRegistryId), } \end{lstlisting} The constraints a profile imposes are carried in \texttt{ProfileConstraints}, which the GC and retention rules (Section~\ref{sec:format:gc}) reference for the mandatory \texttt{RetentionPolicy}: \begin{lstlisting}[language=Rust] pub struct ProfileConstraints { /// Maximum uncompressed operation-envelope block size a reader /// must accept under this profile (default 64 MiB). pub max_uncompressed_block_size: u64, /// The RetentionPolicy this profile declares. Required: the GC /// and retention rules read it from the active profile. pub retention_policy: RetentionPolicy, // Additional constraint fields (permitted compression sets, // required-extension lists) are reserved for a later revision; // a reader MUST tolerate their future addition under the // minor-version compatibility rule. } \end{lstlisting} \begin{requirement} \label{req:format:profile-constraints} Every \texttt{ProfileDeclaration} \MUST{} carry a \texttt{ProfileConstraints}, and every \texttt{ProfileConstraints} \MUST{} include a \texttt{retention\_policy} field. This satisfies the requirement (Section~\ref{sec:format:gc}) that the active conformance profile declare a \texttt{RetentionPolicy}: the policy lives inside the active profile's constraints, not as a free-standing manifest field. When a bundle declares more than one profile, the \texttt{RetentionPolicy} in force is the one declared by the \emph{first} profile in the manifest's \texttt{profile\_declarations} list (declaration order is canonical). A bundle that declares no profile constraints carrying a policy uses the Full profile's default retention policy. \end{requirement} \begin{requirement} Every bundle \MUST{} declare at least one profile in its manifest's \texttt{profile\_declarations}. An implementation \MAY{} open a bundle if it supports any of the declared profiles, subject to the required-extension rules (Section~\ref{sec:format:fwdcompat}). \end{requirement} \begin{requirement} \label{req:format:profileid-discriminants} \texttt{ProfileId} encodes as a fixed \emph{20} bytes: a little-endian \texttt{u32} declaration-order discriminant (\texttt{Full} $= 0$, \texttt{ReadOnly} $= 1$, \texttt{Lite} $= 2$, \texttt{Custom} $= 3$) followed by a 16-byte \texttt{ProfileRegistryId}. The trailing 16 bytes carry the registry id for \texttt{Custom} and are zero for the other three variants; the field is always present, so the encoding is fixed-width (the superblock reserves a fixed slot for it). This assignment is normative and stable. \texttt{ProfileId} participates in superblock selection (Section~\ref{sec:format:bundle}, the load-bearing field set), so its discriminants are part of that comparison. \end{requirement} \section{Compression} \label{sec:format:compression} Each chunk \MAY{} be stored compressed. Compression is per-chunk metadata, not part of content identity. \begin{requirement} Conforming implementations \MUST{} support reading chunks compressed with Zstandard at any level zstd defines. Writers \MAY{} choose to compress or not on a per-chunk basis; uncompressed chunks (\texttt{CompressionAlgorithm::None}) are always permitted. Implementations \MUSTNOT{} require a specific compression algorithm for canonical conformance. Future major format versions \MAY{} introduce additional algorithms via the \texttt{Reserved} variant; such changes require a new format major version. Decompression \MUST{} verify the output length against the declared \texttt{uncompressed\_length} and reject chunks whose decompressed size disagrees. \end{requirement} \section{Streaming Reads} \label{sec:format:streaming} Cold open of a large score \MUST{} be fast: under one second to first interactive frame on a 100-page orchestral score, per the performance requirements (Chapter~\ref{ch:perf}). The format's prelude and chunk-graph design support this. \subsection{Required Locatability} \begin{requirement} The bundle \MUST{} guarantee that: \begin{itemize} \item The fixed header is at offset zero of the file. \item Both superblock slots are at fixed offsets within the first 576 bytes. \item The active manifest is locatable from the active superblock without scanning. \item Operation-envelope blocks, the canonical base snapshot (if any), acceleration snapshots, blobs, and extension chunks are locatable from the manifest's roots without scanning. \item The operation index (if present) provides $O(\log n)$ operation-id lookup; if absent, scanning a small number of blocks suffices. \end{itemize} Locatable means: a reader can determine the offset and length of the chunk in the bundle using only previously read content. \end{requirement} \subsection{Cold Open Procedure} The recommended cold open sequence: \begin{enumerate} \item Read the 64-byte fixed header. Verify magic and CRC. \item Read both 256-byte superblock slots. Verify CRC on each. Select the active slot per the superblock selection rule (Section~\ref{sec:format:bundle}). \item Read and verify the manifest chunk referenced by the active superblock. The manifest is stored uncompressed (Section~\ref{sec:format:manifest-encoding}), so this is a single read-and-parse. \item If the manifest's \texttt{canonical\_base} is \texttt{Some}, read and verify the canonical base snapshot. The snapshot provides the materialized state at its declared causal frontier. \item Stream the operation-envelope blocks listed in \texttt{operation\_roots}. Apply the canonical reduction (Chapter~\ref{ch:semops}) over the envelopes whose \texttt{OperationId} is not covered by the canonical base's \texttt{covers\_causal\_frontier} (if a base is present), or over every envelope in the operation set (if no base is present). The result is the materialized score graph. \item Invoke the layout pipeline (Chapter~\ref{ch:layout-ir}) over the materialized graph, producing the first system's layout. Layout caches (if reachable from the manifest as accelerators) \MAY{} be consulted to skip work; cache misses fall back to fresh layout computation. \item Load remaining regions, envelope blocks, snapshot ranges, and layout caches lazily as the user navigates. \end{enumerate} For unsynchronized first-frame display, implementations \MAY{} present the canonical base's materialized state immediately while the post-frontier reduction completes in the background; user edits \MUST{} be deferred until reduction completes, since the visible state is provisional until then. \subsection{Memory Mapping} \begin{requirement} Implementations \MAY{} memory-map the bundle file for efficient random access to chunks and blobs. The format's content-addressing and chunk immutability guarantees make memory mapping safe: chunks cannot change underneath a reader holding a pointer into them. Superblock slots are 256-byte aligned and never overlap reachable chunk content, so writers updating an inactive superblock cannot corrupt readers accessing the active state. \end{requirement} \section{Binary Format Companion} \label{sec:format:binary} The byte-level encoding (varint conventions, exact field bit widths, record alignment, endianness, string encoding details, struct layouts) is delivered as a companion specification, the Binary Format document. The Binary Format document is normative; an implementation cannot conform to the file format specification without conforming to the Binary Format specification. \subsection{Ratified Convention Baseline} \label{sec:format:codec-baseline} Although the full Binary Format companion is still to be written, the canonical encoding conventions shared by the core, operations, and bundle layers are ratified now, so those three layers do not drift apart before the companion formalizes them. The companion \emph{inherits} this baseline rather than re-deriving it. Every ratified discriminant table, derivation preimage, and primitive encoding is consolidated in Appendix~\ref{app:bytes} (the Canonical Byte-Layout Reference); the companion imports that appendix as its starting point rather than recovering the layouts from the three crates. \begin{requirement} \label{req:format:codec-conventions} The canonical encoding of every composite structure (whole \texttt{Score}, operation envelopes, manifest) \MUST{} follow these conventions: \begin{itemize} \item Integers are little-endian. A boolean is a single \texttt{0}/\texttt{1} byte. \item Counts and the length prefix of every variable-width \emph{leaf} (an identifier, a \texttt{RationalTime}, a string) are \texttt{u32} little-endian. Length-prefixing every leaf makes the decoder width-agnostic: it never infers a boundary from context. \item A tagged union is encoded as a single discriminant byte followed by the variant payload. \item Free-text string fields are length-prefixed UTF-8 and are \emph{not} NFC-folded by the codec: the in-memory value is preserved byte-exact, so \texttt{decode(encode(x)) == x}. Catalog identifiers are NFC-normalized at construction, not in the codec. \end{itemize} Certain fixed hashing preimages deliberately depart from the single-discriminant-byte convention where a wider or order-bearing tag is required --- notably \texttt{TypedObjectId}'s 16-bit big-endian discriminant (Requirement~\ref{req:graph:typed-object-id-discriminants}). Such departures are pinned at their definitions and are not overridden by this baseline. \end{requirement} \begin{requirement} \label{req:format:rationaltime-encoding} The primitive scalar layouts referenced throughout the canonical encodings are ratified as follows: \begin{itemize} \item \texttt{RationalTime} encodes as a sign byte (\texttt{0} = zero, \texttt{1} = positive, \texttt{2} = negative), then the \texttt{u32}-little-endian-length-prefixed big-endian magnitude of the numerator, then the \texttt{u32}-little-endian-length-prefixed big-endian magnitude of the denominator. The value \MUST{} always be stored reduced (lowest terms, positive denominator), so the encoding is canonical: equal rationals encode to equal bytes. \item Wall-clock integers (\texttt{WallClockTime}, \texttt{WallClockDuration}) are little-endian fixed-width integers, matching \texttt{QuantizedCoord}. \end{itemize} \end{requirement} \begin{openquestion} The remainder of the Binary Format companion --- the full composite struct layouts, schema-version wire evolution, and varint details beyond the baseline above --- is under development as a separate specification. Its development follows this chapter and inherits the ratified baseline (Sections~\ref{req:format:codec-conventions} and~\ref{req:format:rationaltime-encoding}); the remaining byte-level decisions depend on the structural decisions specified here. \end{openquestion} \section{Forward References} \begin{itemize} \item The constraint-solver interface (Chapter~\ref{ch:solver}) operates on the in-memory \texttt{ConstrainedLayoutIR}; layout caches stored in the bundle are derivative and not part of the canonical state. \item The full performance requirements that this format must satisfy are in Chapter~\ref{ch:perf}. \item The Binary Format companion specifies byte-level encoding. \item The Text Projection companion specifies the canonical s-expression form. \item The Profile Conformance companion specifies the feature lists per profile. \end{itemize} % =========================================================================== \chapter{Constraint-Solver Interface} \label{ch:solver} This chapter specifies the constraint-solver interface: the \texttt{ConstrainedLayoutIR} the solver consumes, the \texttt{ResolvedLayoutIR} it produces, the constraints it must respect, the determinism contract it must satisfy, the diagnostic information it must emit, and the conformance discipline by which implementations are judged. The chapter deliberately specifies the \emph{interface} and its contracts rather than a particular algorithm. Implementations are free to choose among approaches (Cassowary, linear programming relaxations, custom heuristics, differentiable layout, learned methods) and to evolve their choices over time. Conformance is established by hard-constraint validity, deterministic behavior, diagnostic completeness, and performance and quality on a reference suite of test scores. \section{Design Principles} \label{sec:solver:principles} \begin{description} \item[The interface is normative; global optimality is not.] The solver's input/output contract, its constraint families, its diagnostic surface, and the discipline by which it is evaluated are normative. Global aesthetic optimality (proving no better layout exists) is neither decidable nor practical to verify for arbitrary scores; it is therefore replaced by reference-suite thresholds. \item[Hard constraints are validity; quality metrics are evaluation axes.] Hard constraints are the conditions under which a layout is valid at all. Quality metrics are the axes along which valid layouts are evaluated and compared. The solver \MUSTNOT{} trade off a hard-constraint violation for a better quality score: a layout that violates a hard constraint is not a layout, regardless of its quality vector. \item[Determinism within an implementation; equivalence across implementations.] A given implementation at a fixed version \MUST{} produce identical output for identical input. Different conforming implementations \MAY{} produce different layouts; they conform by satisfying hard constraints, meeting diagnostic contracts, behaving deterministically internally, and passing reference-suite thresholds. Cross-implementation byte-identity is explicitly not required. \item[Pareto frontiers as design target, not conformance obligation.] Solvers \SHOULD{} seek layouts on or near the Pareto frontier of the normative quality metrics. They are \emph{not} required to prove they have done so universally; conformance is reference-suite-based. \item[Deterministic budgets, not wall-clock budgets.] Wall-clock time \MAY{} stop interactive work, but the returned layout's canonical form depends on deterministic counters (iterations, constraint evaluations, search nodes), not on scheduler luck. \item[Incremental solving as observational equivalence.] Incremental re-solving \MUST{} be observationally equivalent to full solving of the declared invalidation scope under the same profile and budget. Implementations are free to widen scopes conservatively but \MAY NOT{} return layouts that diverge from the canonical scoped result. \item[Structured diagnostics, never silent corruption.] Solver failures are first-class outputs with diagnostic information sufficient for the editor to surface meaningful feedback. The score graph is never invalidated by a solver failure; a solver error means ``we could not produce a layout,'' not ``the score is broken.'' \end{description} \section{The Solver Interface} \label{sec:solver:interface} \begin{lstlisting}[language=Rust] pub trait ConstraintSolver: Send + Sync { /// The solver's identifying tier. Used by readers/editors to /// decide whether this solver is sufficient for the active /// score's declared solver tier requirement. fn tier(&self) -> SolverTier; /// The solver's implementation version. Identical input plus /// identical version produces identical output (within- /// implementation determinism). fn version(&self) -> SolverVersion; /// Solve from scratch. fn solve( &self, input: &ConstrainedLayoutIR, config: &SolverConfig, ) -> SolveReport; /// Solve incrementally over the declared invalidation scope. fn solve_incremental( &self, input: &ConstrainedLayoutIR, prior: &SolverState, invalidations: &InvalidationSet, config: &SolverConfig, ) -> SolveReport; } pub struct SolverConfig { /// Conformance profile under which to solve. Selects metric /// thresholds, profile-specific constraints, and the active /// extension set. pub profile: SolverProfile, /// Deterministic budget. Wall-clock time is advisory only. pub budget: SolverBudget, /// Tie-breaking weights for selecting among layouts of /// equivalent quality. pub tie_breaking: TieBreakingWeights, } pub struct SolverBudget { /// Maximum solver iterations (algorithm-defined unit; the /// reference algorithm counts Cassowary pivots). pub max_iterations: u64, /// Maximum search-tree nodes explored, for algorithms that /// perform discrete search. pub max_nodes: u64, /// Maximum constraint-evaluation count. pub max_constraint_evaluations: u64, /// Advisory wall-clock cap, in milliseconds. The solver MAY /// stop early when this is exceeded, but the layout it /// returns under stoppage MUST be the same layout that the /// deterministic budget would have produced at the /// corresponding deterministic counter state. Wall-clock time /// MUST NOT change the canonical layout. pub advisory_wall_time_ms: Option, } pub struct SolverBudgetUsed { pub iterations: u64, pub nodes: u64, pub constraint_evaluations: u64, pub wall_time_ms: u64, } \end{lstlisting} \begin{requirement} \texttt{solve} and \texttt{solve\_incremental} \MUST{} be pure functions of their inputs within the determinism contract (Section~\ref{sec:solver:determinism}). They \MUSTNOT{} consult external state (system clock, environment, thread scheduling, random sources) in ways that influence the returned layout. Implementations \MAY{} use multi-threaded execution internally, but the result \MUST{} be identical to a single-threaded execution under the same deterministic budget. \end{requirement} \section{The Solver Report} \label{sec:solver:report} Every solver invocation returns a \texttt{SolveReport}. There is no separate ``error'' return; failures and partial successes appear as report variants distinguished by \texttt{SolveStatus}. \begin{lstlisting}[language=Rust] pub struct SolveReport { pub status: SolveStatus, /// Whether every hard constraint is satisfied. pub satisfied_hard_constraints: bool, /// The layout produced. Always present; under failure /// statuses, the layout is diagnostic-only and MUST NOT be /// used as if it were valid. pub layout: ResolvedLayoutIR, /// Updated solver state for subsequent incremental calls. pub state: SolverState, /// Unsatisfied hard constraints, if any. Empty under /// SolveStatus::Solved. pub unsatisfied_constraints: Vec, /// Non-fatal warnings about the solution. pub warnings: Vec, /// Quality metric vector for the returned layout. pub metric_vector: QualityMetricVector, /// Budget consumed during this solve. pub budget_used: SolverBudgetUsed, } pub enum SolveStatus { /// All hard constraints satisfied, target quality reached. Solved, /// All hard constraints satisfied, but warnings were /// generated (e.g., a soft constraint had a large violation, /// or a layout decision was unusual and worth surfacing). SolvedWithWarnings, /// Deterministic budget exhausted before reaching target /// quality. The returned layout MUST still satisfy all hard /// constraints; it simply may not be optimal. If hard /// constraints could not be satisfied within budget, the /// status is Unsatisfiable, not PartialBudgetExhausted. PartialBudgetExhausted, /// Hard constraints cannot be simultaneously satisfied. The /// returned layout is diagnostic-only and MUST NOT be /// presented as a valid layout. Unsatisfiable, /// Solver bug or unexpected error. The returned layout is /// diagnostic-only. Editors SHOULD surface this as a defect /// to report. InternalError, } pub struct SolverWarning { pub kind: SolverWarningKind, pub affected_objects: Vec, pub message: String, } pub enum SolverWarningKind { LargeSoftConstraintViolation { constraint: ConstraintId, magnitude: f64 }, UnusualLayoutDecision(String), QualityFloorApproached { metric: QualityMetricKind }, ExtensionWarning(ExtensionWarningId), } \end{lstlisting} \begin{requirement} The \texttt{SolveReport.layout} field is always populated, but its authority depends on \texttt{status}: \begin{itemize} \item \texttt{Solved} or \texttt{SolvedWithWarnings}: the layout is a valid layout that the editor may render. \item \texttt{PartialBudgetExhausted}: the layout is a valid layout (\texttt{satisfied\_hard\_constraints} is \texttt{true}) and the editor may render it, while noting that quality is below the profile's target. \item \texttt{Unsatisfiable} or \texttt{InternalError}: the layout is diagnostic-only. Editors \MAY{} render it for the user to see what failed, but \MUST{} clearly indicate the layout is not authoritative. \end{itemize} A solver \MUSTNOT{} return \texttt{PartialBudgetExhausted} with \texttt{satisfied\_hard\_constraints == false}. If hard constraints cannot be satisfied within the budget, the correct status is \texttt{Unsatisfiable}, with the partial layout marked as such. \end{requirement} \begin{requirement} \label{req:solver:subconformant-report} \textbf{Sub-conformant report shape (ratified Pass 12).} A below-conformance passthrough solver (\texttt{SolverTier::Stub}) presented with declared constraints it does not evaluate reports \texttt{SolvedWithWarnings} with \texttt{satisfied\_hard\_constraints == false} and a warning naming the unevaluated-constraint condition. The layout stays renderable; the report makes no conformance claim and satisfies no minimum-tier requirement. This is the one sanctioned case in which a renderable status carries \texttt{satisfied\_hard\_constraints == false}: the field reports \emph{evaluated} satisfaction, and a non-evaluating solver has nothing to claim. \end{requirement} \section{Constraint Families} \label{sec:solver:families} The solver consumes constraints in normalized form. The constraint families enumerated here are the normative core; implementations \MAY{} support additional families via \texttt{LayoutConstraint::Registered}. \subsection{Strength Levels} \begin{lstlisting}[language=Rust] pub enum ConstraintStrength { /// Hard constraint. The solver MUST satisfy this constraint, /// or return Unsatisfiable. Required, /// Soft constraint with an associated weight. The solver /// minimizes the weighted violation when optimizing. Preferred { weight: f64 }, } \end{lstlisting} \begin{requirement} A solver \MUSTNOT{} treat a \texttt{Required} constraint as if it were \texttt{Preferred} for any reason, including for quality optimization. A layout that violates a \texttt{Required} constraint is not a valid layout, regardless of its quality vector. \end{requirement} \begin{requirement} \label{req:solver:kind-strength} \textbf{Strength is kind-determined (ratified Pass 12).} A constraint instance carries no strength field; its strength is determined by its kind. Break constraints take their strength from \texttt{BreakKind}: \texttt{Hard} $\to$ \texttt{Required}, \texttt{Soft} $\to$ \texttt{Preferred} with weight $1.0$. Every other core constraint family normalizes to \texttt{Required}. \texttt{Registered} constraint kinds normalize conservatively to \texttt{Required}. A future constraint family declares its strength (and weight, if \texttt{Preferred}) in its normative definition --- the strength channel is the \emph{kind}, not the instance. \end{requirement} \subsection{Normative Constraint Families} The constraint catalog (spring, collision, alignment, containment, break, aesthetic, extension) is specified in Chapter~\ref{ch:layout-ir}. Implementations \MUST{} support every family normatively required by the active solver tier (Section~\ref{sec:solver:tiers}). \section{Quality Metrics} \label{sec:solver:quality} The solver's output is evaluated against a normative set of quality metrics. Each metric is normalized to the closed interval $[0.0, 1.0]$, with $0.0$ representing the best achievable value and $1.0$ representing the worst tolerable value before a profile threshold flags the metric as failing. \begin{lstlisting}[language=Rust] /// A quality metric value normalized to [0.0, 1.0]. /// Lower is always better. pub struct NormalizedMetric(pub f64); impl NormalizedMetric { /// Construct, panicking if the value is not finite or is out /// of range. Conforming implementations MUST construct only /// valid NormalizedMetric values. pub fn new(value: f64) -> Self { assert!(value.is_finite()); assert!(0.0 <= value && value <= 1.0); NormalizedMetric(value) } } pub struct QualityMetricVector { pub collision_penalty: NormalizedMetric, pub spacing_distortion: NormalizedMetric, pub slur_shape_penalty: NormalizedMetric, pub beam_slope_penalty: NormalizedMetric, pub vertical_density_penalty: NormalizedMetric, pub system_break_penalty: NormalizedMetric, pub page_fill_efficiency: NormalizedMetric, pub casting_off_quality: NormalizedMetric, pub symbol_density_uniformity: NormalizedMetric, /// Additional metrics contributed by extensions. Each MUST be /// a valid NormalizedMetric (finite, in [0.0, 1.0], lower is /// better). pub extension_metrics: Vec, } pub struct ExtensionMetric { pub metric_id: ExtensionMetricId, pub value: NormalizedMetric, } \end{lstlisting} \begin{requirement} Every \texttt{NormalizedMetric} value \MUST{} satisfy: \begin{itemize} \item It is finite (not NaN, not infinity). \item It lies in the closed interval $[0.0, 1.0]$. \item Lower values denote better layouts; higher values denote worse layouts. \end{itemize} Per-metric normalization functions (mapping raw measurements to $[0.0, 1.0]$) are specified in the Quality Metric Catalog companion document. Implementations \MUST{} use the catalog's normalization; arbitrary normalization is non-conforming. Extension metrics \MAY{} define their own normalization function but \MUSTNOT{} change orientation or range: $0.0$ remains best, $1.0$ remains worst, and values must remain finite within range. \end{requirement} \subsection{Pareto Frontiers as Design Target} Solvers should seek layouts on or near the Pareto frontier of the normative metrics. This is the design intent. It is not the conformance requirement. \begin{requirement} The normative metric set defines the axes along which layout quality is evaluated. Solvers \SHOULD{} produce Pareto-efficient layouts with respect to these metrics: layouts for which no metric can be improved without worsening another. Because global Pareto optimality is not practically verifiable for arbitrary scores, conformance is determined by reference-suite thresholds (Section~\ref{sec:solver:conformance}) rather than by proof of Pareto-optimality. When multiple Pareto-equivalent layouts exist, the configured \texttt{TieBreakingWeights} select among them. Tie-breaking is deterministic. \end{requirement} \begin{lstlisting}[language=Rust] pub struct TieBreakingWeights { pub collision: f64, pub spacing: f64, pub slur_shape: f64, pub beam_slope: f64, pub vertical_density: f64, pub system_break: f64, pub page_fill: f64, pub casting_off: f64, pub symbol_density: f64, } \end{lstlisting} \begin{requirement} Tie-breaking weights \MUST{} have normative defaults specified in the Quality Metric Catalog. Implementations \MAY{} permit users to customize these weights; the defaults represent the reference engraving aesthetic and are the basis for reference-suite conformance. \end{requirement} \section{Conformance Tiers} \label{sec:solver:tiers} Solver conformance is graded into tiers, each declaring a subset of features the implementation supports. Tiers are \emph{orthogonal} to file-format profiles (Chapter~\ref{ch:format}); a document may declare both an authoring file profile and a minimum solver tier, and the two axes are independent. \begin{lstlisting}[language=Rust] pub enum SolverTier { /// Not a conformance tier: an interface-only / passthrough /// solver that evaluates no constraints and computes no /// quality metrics. Ordered below every conformant tier. Stub, /// Minimal Layout Solver. Minimal, /// Standard Engraving Solver. Standard, /// Advanced / Extension-Aware Solver. Advanced, } \end{lstlisting} The \texttt{Stub} variant exists so scaffolding, harnesses, and not-yet-conformant pipelines can label themselves honestly: it makes no conformance claim, and a document requiring any conformant tier is never satisfied by a solver declaring \texttt{Stub}. \subsection{Minimal Layout Solver} \begin{requirement} A solver claiming the Minimal tier \MUST{}: \begin{itemize} \item Satisfy every hard constraint or return \texttt{Unsatisfiable} (validity conformance). \item Be deterministic within its declared implementation version (Section~\ref{sec:solver:determinism}). \item Produce well-formed \texttt{SolveReport} values with accurate \texttt{satisfied\_hard\_constraints}, \texttt{unsatisfied\_constraints}, and metric vectors (diagnostic conformance). \item Support common-music-notation regions, metric time, and the standard constraint families. \item Pass the Minimal subset of the reference suite. \end{itemize} A Minimal solver \MAY{} produce mediocre aesthetic quality; metric thresholds for the Minimal tier are relaxed relative to the Standard tier. \end{requirement} \subsection{Standard Engraving Solver} \begin{requirement} A solver claiming the Standard tier \MUST{}: \begin{itemize} \item Satisfy every Minimal-tier obligation. \item Meet the Standard-tier metric thresholds on the reference suite for every quality metric. \item Support the incremental solving contract with at least Measure-local invalidation scope (Section~\ref{sec:solver:incremental}). \item Support every Standard-tier constraint family declared in the Quality Metric Catalog. \end{itemize} The Standard tier corresponds to ``professional engraving quality'' for common-practice notation. \end{requirement} \subsection{Advanced / Extension-Aware Solver} \begin{requirement} A solver claiming the Advanced tier \MUST{}: \begin{itemize} \item Satisfy every Standard-tier obligation. \item Support proportional and aleatoric region layout, not only metric regions. \item Support microtonal accidentals and their interactions with adjacent glyphs. \item Support extension-contributed constraint families and extension metric thresholds for each registered extension whose layout requirements are part of the Advanced reference suite. \item Support at least System-local invalidation scope under incremental solving. \end{itemize} \end{requirement} \subsection{Tier Declaration and Document Compatibility} \begin{requirement} Documents \MAY{} declare a minimum solver tier required for faithful engraving in their profile declarations (Chapter~\ref{ch:format}). A solver with tier lower than the document's declared minimum \MAY{} open the document but \SHOULD{} surface a clear warning that engraving quality may not match the document's intent. Solver tier is independent of file-format profile. A document authored under the \texttt{Lite} file profile \MAY{} declare a minimum solver tier of \texttt{Standard}; a document authored under the \texttt{Full} file profile \MAY{} declare no minimum tier and be displayable by a \texttt{Minimal} solver. \end{requirement} \section{Determinism} \label{sec:solver:determinism} The solver's determinism contract has two parts. \emph{Within- implementation determinism} is a strict byte-equality obligation; \emph{cross-implementation conformance} is reference-suite-based. \subsection{Within-Implementation Determinism} \begin{requirement} A solver implementation at a fixed version \MUST{} produce byte-for-byte identical \texttt{SolveReport} output for identical inputs, where ``identical inputs'' means: \begin{itemize} \item Identical \texttt{ConstrainedLayoutIR}. \item Identical \texttt{SolverConfig} (profile, budget, tie-breaking weights). \item Identical font and glyph metrics (referenced by version and content hash). \item Identical reduction algorithm version (Chapter~\ref{ch:semops}). \item For incremental calls: identical prior \texttt{SolverState} and identical invalidation set. \item Identical solver implementation version (i.e., the same binary). \end{itemize} Determinism \MUST{} hold across runs on the same machine, across cold and warm starts, and across hardware threads. Implementations \MUSTNOT{} use fast-math flags, processor- dependent SIMD without canonical-output guarantees, or any compiler options that vary floating-point results across builds on the same platform. Where parallelism is used internally, the solver \MUST{} serialize the merge step so that thread scheduling does not affect output. Deterministic budgets \MUST{} be enforced from shared counters that capture all parallel contributions. \end{requirement} \subsection{Cross-Implementation Conformance} \begin{requirement} Cross-implementation determinism (byte-equality of outputs across different conforming solvers) is \emph{not} required. Different conforming solvers \MAY{} produce different layouts for the same input. Conformance across implementations is established by: \begin{enumerate} \item All hard constraints satisfied on every reference suite score. \item The implementation is internally deterministic per the within-implementation rule above. \item \texttt{SolveReport} values are well-formed and diagnostically accurate. \item For each reference-suite score, every quality metric is within the tier's declared threshold. \end{enumerate} \end{requirement} \begin{rationale} Cross-implementation byte equality would freeze the ecosystem to whatever single algorithm first claimed conformance, and would preclude algorithmic innovation. Reference-suite thresholds give users a meaningful quality floor while permitting genuine implementation diversity. Within- implementation determinism remains essential for reproducible builds, regression testing, and predictable behavior under collaborative editing. \end{rationale} \section{Incremental Solving} \label{sec:solver:incremental} \subsection{Invalidation Scopes} \begin{lstlisting}[language=Rust] pub enum InvalidationScope { /// A single object's layout depends only on local context. ObjectLocal, /// Effects bounded within a single measure. MeasureLocal, /// Effects bounded within a single system. SystemLocal, /// Effects bounded within a single page. PageLocal, /// Effects bounded within a single region. RegionLocal, /// Effects may propagate across the entire score. WholeScore, } pub struct InvalidationSet { /// Declared scope. The solver MAY widen this scope /// conservatively; it MUST NOT narrow it. pub scope: InvalidationScope, /// Specific invalidated entries within the declared scope. pub slots: Vec, pub bands: Vec, pub constraints: Vec, pub glyphs: Vec, } \end{lstlisting} \subsection{Observational Equivalence} \begin{requirement} For any invalidation set with declared scope $S$, \texttt{solve\_incremental} \MUST{} produce a layout observationally equivalent to \texttt{solve} called on the same input restricted to $S$, under the same profile, the same configuration, and the same deterministic budget. ``Observational equivalence'' means byte-identical \texttt{ResolvedLayoutIR} output for objects within $S$, and unchanged output for objects outside $S$. The solver \MAY{} widen the scope conservatively: an \texttt{ObjectLocal} invalidation may be solved as \texttt{MeasureLocal} if that simplifies the implementation, provided the widened result is also observationally equivalent to full solving at the widened scope. The solver \MAY NOT{} narrow the scope. If \texttt{SystemLocal} effects might propagate (e.g., an edit at the system's end shifts subsequent systems' contents), the invalidation \MUST{} be declared at least \texttt{SystemLocal} or higher. \end{requirement} \subsection{Propagation Documentation} \begin{requirement} Implementations \MUST{} document, per their published conformance contract, the maximum invalidation scope they can service for each constraint family. The documented bound is the implementation's contract for how far edit effects propagate before requiring whole-score re-solving. \end{requirement} \section{Conformance: The Reference Suite} \label{sec:solver:conformance} Solver conformance is established by performance on a normative reference suite of test scores. Each tier has its own subset of the suite and its own metric thresholds. \begin{requirement} The Reference Suite is delivered as a companion specification. Each suite entry consists of: \begin{itemize} \item A test score in canonical \texttt{.musc} form. \item Per-tier inclusion: whether the entry is required for Minimal, Standard, or Advanced conformance. \item Per-tier metric thresholds: the maximum permitted \texttt{NormalizedMetric} value for each quality metric. \item Optional fixed-expectation tests: specific layout properties (a particular bar's width, a specific system break location) that all conforming solvers \MUST{} reproduce. Used sparingly, only where the suite intends to pin an unambiguous expectation. \end{itemize} A solver claiming a given tier \MUST{} pass every entry required at that tier. Failure on any single entry is conformance failure at the claimed tier. Suite versions are tied to specification versions; conforming implementations \MUST{} declare which suite version they pass. \end{requirement} \begin{rationale} The reference suite makes conformance checkable. The earlier universal-Pareto formulation quantified over all possible layouts of every possible score, which is undecidable. Reference-suite thresholds replace ``no better layout exists'' with ``this layout is within tolerance on these specific scores,'' which is testable, falsifiable, and operationally useful. The cost is that conformance is only as good as the suite's coverage; that is acceptable, because suite coverage is visible, evolvable, and can be expanded as the spec matures. \end{rationale} \section{Reference Algorithm} \label{sec:solver:reference-algo} The reference algorithm is explanatory and diagnostic. Its purpose is to disambiguate constraint interactions where prose alone might leave a choice open, and to generate the expected outputs published with the reference suite. It is \emph{not} the normative algorithm and \emph{not} the aesthetic floor. \begin{requirement} Conforming implementations are \emph{not} required to use the reference algorithm or to reproduce its layouts, except where a reference-suite entry explicitly fixes an expected behavior via a fixed-expectation test. Implementations \MAY{} choose any algorithm satisfying the contract: hard constraints, determinism, diagnostics, reference-suite thresholds. \end{requirement} The reference algorithm is described in a non-normative companion document. A natural starting point uses the Cassowary linear- arithmetic constraint solver for the linear layer (positions, alignments, containment) together with a separate numerical optimization layer for aesthetic constraints (beam slopes, slur curvatures) that are nonlinear in nature. Hybrid approaches and alternative architectures (differentiable layout, constraint programming, learned methods) are all conforming choices. \section{Forward References} \begin{itemize} \item End-to-end performance requirements, including frame budgets that the solver participates in, are in Chapter~\ref{ch:perf}. Solver performance is measured in the context of the full layout pipeline. \item The Quality Metric Catalog companion document specifies the exact normalization functions for each metric, the default tie-breaking weights, and the per-tier thresholds. \item The Reference Suite companion document specifies the test scores, per-tier inclusion, and per-tier metric thresholds. \item The reference-algorithm companion document is non-normative and describes one starting-point implementation. \end{itemize} % =========================================================================== \chapter{Performance Requirements} \label{ch:perf} This chapter specifies performance contracts for the \emph{core layer}. Targets are normative; they bound what conforming implementations promise and what tests measure for conformance. The chapter restates targets introduced informally in earlier chapters and adds the measurement methodology. \section{Core/Product Boundary} \label{sec:perf:boundary} Chapter~\ref{ch:intro} excluded the editor UI, the audio engine, the renderer beyond the layout IR, and platform integrations from the core's scope. Many performance properties of a working notation system depend on those out-of-scope components: first interactive frame after launch involves UI bootstrapping, edit response involves UI event handling, scrolling and selection involve a real renderer, audio acquisition involves a working audio engine. The core cannot promise end-to-end timings for behavior it does not own. This chapter therefore distinguishes three obligation classes: \begin{description} \item[Core obligations.] The core data structures, layout IR, solver, and file format \MUST{} admit the stated bounds. The core's own work (reduction, layout, file writes, snapshot acquisition) \MUST{} fit within the budgets allocated to it in the end-to-end frame budget. The core \MUST{} expose APIs suitable for each downstream component to do its own work inside its remaining budget (e.g., a lock-free immutable snapshot API for audio engines, an incremental invalidation interface for renderers, a cancellable solve API for UIs). \item[Product obligations.] End-to-end UI, audio, and rendering targets (first interactive frame after launch, end-to-end edit-to-pixel latency, end-to-end scroll smoothness, audio-thread snapshot acquisition latency under load) are the obligation of the product layers built atop the core. Those targets are specified in product-specific companion documents, not here. \item[Performance Reference Suite obligations.] The Performance Reference Suite (Appendix~\ref{app:deferred}) measures core behavior only: layout time per system, file write/read time, reduction throughput, solver budget consumption, snapshot acquisition latency. End-to-end UI traces are out of scope for the core's reference suite. \end{description} \begin{requirement} Where this chapter states a performance target, the target is a \emph{core obligation} unless explicitly labeled product obligation. Core obligations \MUST{} be satisfiable by a conforming core implementation independent of the product layers built atop it; product-layer code is permitted to consume budget on top of the core's portion, but the core's own portion \MUST{} fit within the stated bound. \end{requirement} \section{Design Principles} \label{sec:perf:principles} \begin{description} \item[Targets are part of the contract.] Core performance requirements are not aspirations. A conforming core implementation \MUST{} meet the stated targets on the reference hardware profile under the stated measurement conditions. \item[Frame budgets dominate.] User experience is ultimately governed by frame timing. The core's portion of each frame budget is stated explicitly; the product layer's portion is specified separately. \item[Reference hardware profile.] All targets are stated against a specified hardware profile. Implementations \MAY{} document additional profiles (lower-end mobile, higher-end workstation); the reference profile is the basis of conformance. \item[Measurement methodology is normative.] Without specified measurement methodology, performance targets are unenforceable. This chapter specifies how each target is measured. \item[Tail latency matters.] Average performance is insufficient; targets are stated as percentiles (typically p99 or worst-case) to reflect what users actually experience. \end{description} \section{Reference Hardware Profile} \label{sec:perf:hardware} The reference hardware profile defines the platform against which performance conformance is measured. Implementations \MAY{} target additional profiles; this profile is the basis of conformance. \begin{lstlisting} Reference Hardware Profile (2026 baseline): Desktop reference: - Apple silicon (M-series) or x86-64 equivalent - 8 performance cores (or equivalent throughput) - 16 GB RAM - NVMe SSD storage - Discrete or integrated GPU with Metal/Vulkan support - Display: 1440p at 60 Hz minimum Tablet reference: - iPad Pro M-series or equivalent - 8 GB RAM minimum - SSD storage - Display: 120 Hz Pro Motion or equivalent \end{lstlisting} \begin{requirement} Conforming implementations \MUST{} meet the targets in this chapter on the reference hardware profile. Implementations \MAY{} miss targets on hardware below the reference profile (e.g., older devices, low-end embedded targets); such gaps \MUST{} be documented in the implementation's published performance contract. The reference profile is updated periodically as commodity hardware evolves. The version of the reference profile against which an implementation conforms \MUST{} be declared. \end{requirement} \section{Frame Budgets} \label{sec:perf:frame} \subsection{Interactive Edit Latency} \begin{requirement} The \emph{core's portion} of a single-system edit (operation envelope construction, reduction, incremental layout through \texttt{ResolvedLayoutIR}) \MUST{} complete within one frame (16.7\,ms at 60\,Hz) at the p99 percentile, measured on the reference hardware profile. This includes the case where the affected system is on a 100-page orchestral score: the core's incremental layout machinery (Chapter~\ref{ch:layout-ir}) and the constraint solver's incremental contract (Chapter~\ref{ch:solver}) jointly satisfy this requirement. On 120\,Hz displays (tablet reference), the core's portion \SHOULD{} complete within 8.3\,ms at p99. This is not required for conformance but is the target for first-class tablet performance. End-to-end edit-to-pixel latency (input handling, hit testing, render submission, display flip) is a product-layer obligation. The core's contribution is bounded by this target so that the product layer has the remaining frame budget to do its work. \end{requirement} \subsection{Multi-System Edits} \begin{requirement} Edits affecting up to ten systems (e.g., a transposition over a substantial passage) \MUST{} complete within four frames (66.7\,ms at 60\,Hz) at p99. The four-frame budget gives the editor headroom to display a transient ``thinking'' indication without violating perception of responsiveness. \end{requirement} \subsection{Full Re-Layout} \begin{requirement} Full re-layout of a 100-page orchestral score (triggered, for example, by a tuning system change applied score-wide) \SHOULD{} complete within two seconds at p99. This is a soft target; the hard target on full re-layout is that the operation be cancellable: a user beginning a different edit \MUST{} cause the full re-layout to abort and the new edit to proceed. \end{requirement} \section{Cold Open} \label{sec:perf:open} \begin{requirement} The \emph{core's portion} of cold open of a 100-page orchestral score from a \texttt{.musc} bundle \MUST{} reach \emph{first-system-ready} state within one second on the reference hardware profile. First-system-ready is defined as: the prelude has been read, the active superblock and manifest validated, the canonical base snapshot (if any) acquired, post-frontier envelopes reduced for at least the first system's coverage, and the first system's \texttt{ResolvedLayoutIR} produced. Rendering, hit-testing, and UI bootstrapping that build atop this state are product-layer obligations. Background loading of remaining systems \MAY{} continue after first-system-ready and \MUSTNOT{} block user interaction served from already-loaded state. not-yet-visible systems is permitted; full-score loading need not complete in one second. \end{requirement} \subsection{Cold Open Measurement} Cold open is measured from process start (or, for warm processes opening a new score, from the open API invocation) to the first frame containing fully rendered content for the user's initial viewport. \subsection{Streaming Read Discipline} \begin{requirement} The cold open target \MUST{} be achieved through streaming reads per Chapter~\ref{ch:format}: the implementation \MUSTNOT{} load the full score into memory before displaying the first system. Implementations failing to meet the target by eager loading are non-conforming. \end{requirement} \section{Memory Budgets} \label{sec:perf:memory} \begin{requirement} Steady-state resident memory for a 100-page orchestral score \SHOULD{} remain below 500\,MB on the reference hardware profile, exclusive of evictable caches. Evictable caches \MAY{} push total resident memory higher; they \MUST{} be evicted under memory pressure without compromising correctness. \end{requirement} \subsection{Memory Categories} For measurement, memory is partitioned into: \begin{description} \item[Core data.] The score graph, attachments, indexes, and operation envelope set. Cannot be evicted without losing state. \item[Layout cache.] The cached IR stages. Evictable; rebuilds on demand but at performance cost. \item[Glyph and font caches.] Glyph render data and metric caches. Evictable; small-to-moderate size. \item[Audio buffers and reference tracks.] Audio data loaded for playback or reference. Evictable. \item[Implementation overhead.] Runtime, allocator metadata, OS-level mappings. \end{description} \begin{requirement} Implementations \MUST{} expose the core-data memory consumption separately from evictable caches. Reporting tools \MUST{} be able to distinguish them. The 500\,MB target applies to core data only. \end{requirement} \subsection{Per-Object Memory Discipline} \begin{requirement} Implementations \MUSTNOT{} embed glyph metrics in IR objects (Chapter~\ref{ch:layout-ir}). Implementations \MUSTNOT{} embed spelling-attachment payloads on pitch objects (Chapter~\ref{ch:pitch}); attachments are externally indexed. These discipline requirements bound per-object memory at known upper limits. \end{requirement} \section{Core Data Structures for Audio Thread Use} \label{sec:perf:audio} The audio engine is outside the core specification. The core's obligation is to expose data structures that permit real-time audio safety; audio-engine performance is the obligation of the product audio layer (Section~\ref{sec:perf:boundary}). \begin{requirement} The core's read-only data structures consumed by an audio thread (pitch resolution, tempo map lookup, event arena queries) \MUST{} be designed for allocation-free, lock-free access. Specifically: \begin{itemize} \item Pitch frequency resolution given a tuning system \MUST{} be performable without allocation. \item Tempo-map conversion (musical to wall-clock and vice versa) \MUST{} be performable without allocation. \item Time-indexed event lookup for playback \MUST{} be performable without allocation. \end{itemize} These are properties of the core's data layouts, not of any particular audio engine. A product-layer audio engine that consumes them is responsible for its own real-time discipline (no syscalls on the audio thread, no priority inversion, no unbounded loops); the core only promises that its data structures do not preclude that discipline. \end{requirement} \subsection{Snapshot API} \begin{requirement} The core \MUST{} provide an immutable-snapshot API: the ability for any thread to acquire a read-only view of the score graph at a consistent point in time, suitable for use by an audio engine. Snapshot acquisition by the snapshot reader \MUST{} be lock-free in the sense that it never blocks on edit-thread progress: an edit in flight \MUSTNOT{} prevent the snapshot reader from obtaining a valid (possibly older) snapshot. Snapshot \emph{release} \MAY{} be deferred to a non-audio thread. The snapshot mechanism is implementation-defined (double-buffered immutable copies, persistent data structures, reference-counted immutable trees, hazard pointers); only the externally observable lock-free acquisition property is normative here. End-to-end audio-thread acquisition latency under load is a product-layer obligation; the core's contribution is bounded by the lock-free guarantee. \end{requirement} \section{Constraint-Solver Performance Targets} \label{sec:perf:solver} \begin{requirement} The constraint solver (Chapter~\ref{ch:solver}) \MUST{} satisfy: \begin{itemize} \item Incremental solving of a single-system edit completes within 10\,ms at p99 on the reference hardware profile. (This is a tighter target than the overall frame budget; the remaining 6.7\,ms accommodates the rest of the pipeline.) \item Cold solving of a 100-page orchestral score completes within two seconds at p99. \item Incremental solving propagation is bounded as documented in the implementation's published contract. \end{itemize} \end{requirement} \section{File Format Performance} \label{sec:perf:format} \begin{requirement} File format operations \MUST{} satisfy: \begin{itemize} \item Bundle write of a typical edit (one or more operation envelopes appended to the operation-envelope block stream, manifest rewrite, superblock flip) completes within 50\,ms at p99 on the reference hardware profile. \item Bundle read of the manifest and bootstrap chunks (sufficient for first interactive frame) completes within 200\,ms at p99 for a 100-page orchestral score. \item Operation-envelope reduction rate \MUST{} exceed 10{,}000 envelopes per second on the reference hardware profile, measured during cold reduction from a fresh canonical base. \end{itemize} \end{requirement} \section{Measurement Methodology} \label{sec:perf:methodology} \begin{requirement} Performance conformance \MUST{} be measured using: \begin{itemize} \item A reference test corpus including the conformance suite from Chapter~\ref{ch:solver}, augmented with edit traces representative of typical authoring sessions. \item Measurements at the p99 percentile across at least 1000 iterations per scenario. \item Warm runs after a cold warm-up phase, unless the target is explicitly cold (cold open, cold load). \item Release builds with full optimization enabled, but without fast-math or precision-compromising flags. \item The reference hardware profile, with no other significant load on the system during measurement. \end{itemize} Implementations \MUST{} publish their measurement results against this methodology for conformance claims. \end{requirement} \section{Regression Testing} \label{sec:perf:regression} \begin{requirement} Conforming implementations \SHOULD{} maintain continuous performance regression testing against the reference corpus, with alerts for regressions exceeding 10\% on any target. The regression suite \SHOULD{} be a precondition for releases. \end{requirement} \section{Forward References} \begin{itemize} \item The full performance reference corpus is delivered as a companion specification, the Performance Reference Suite. \item Audio engine performance targets (latency, jitter, stream discipline) are specified in the audio engine specification, outside this document. \item Network and synchronization performance for the collaboration layer is specified in the collaboration specification, outside this document. \end{itemize} % =========================================================================== \chapter{Extension Points} \label{ch:extension} This chapter consolidates the extension points specified across prior chapters into a single reference. Each extension point admits implementation-defined or plugin-contributed content that the core treats opaquely, subject to the contracts stated here. The chapter is a navigation aid rather than a source of new requirements: every extension point referenced here is defined in its originating chapter, and the originating definition is authoritative. This chapter pulls the points together for plugin authors and future specification editors. \section{Design Principles} \label{sec:ext:principles} \begin{description} \item[Extensibility without forking.] Every extension point permits content to be added without modifying the core specification or its implementations. Plugins extend; they do not fork. \item[Identifiers, not strings.] Extension points are identified by typed registry identifiers, not free-form strings. Registries are explicit, versioned, and discoverable. \item[Contracts, not contents.] Extension points specify what an extension \emph{must satisfy} (interfaces, invariants, reduction rules, performance bounds) rather than what an extension \emph{must contain}. \item[Capability-bound at the runtime.] Plugin runtime permissions (read score, write score, network, filesystem) are declared per extension and granted per user. This is enforced by the plugin runtime, which is outside this specification; the extension points are designed to admit such enforcement. \item[Forward compatibility.] Extensions referenced in a stored score \MUST{} be preserved across reads and writes even when the reading implementation does not understand them (per Chapter~\ref{ch:format}). Extensions thus survive distribution even where they cannot be exercised. \end{description} \section{Notation Grammar Extensions} \label{sec:ext:grammar} The notation grammar layer supports extension at several points: \begin{description} \item[\texttt{PositionStructure::Registered}] (Chapter~\ref{ch:tuning}). Custom position structures for pitch spaces whose organization does not fit the chromatic, diatonic-over-chromatic, or JI lattice families. Used for maqam, gamelan, raga, and other non-Western grammars. \item[\texttt{IntervalAlgebra::Registered}] (Chapter~\ref{ch:tuning}). Custom interval algebras for grammar-specific arithmetic. \item[\texttt{TranspositionBehavior::Registered}] (Chapter~\ref{ch:tuning}). Custom transposition rules. \item[\texttt{SpellingAlgorithmId}] (Chapter~\ref{ch:pitch}). Registered spelling-inference algorithms for the spelling pre-pass. \item[\texttt{NominalRegistry}] (Chapter~\ref{ch:tuning}). Score-level or grammar-level extensions to the catalog of named position-letters. \item[\texttt{AccidentalRegistry}] (Chapter~\ref{ch:tuning}). Score-level extensions to the accidental catalog, subject to the registry's \texttt{extensible} flag. \end{description} \section{Tuning System Extensions} \label{sec:ext:tuning} \begin{description} \item[\texttt{TuningFunctionId}] (Chapter~\ref{ch:tuning}). Registered procedural tuning functions (historical temperaments, custom meantone variants, generative tunings). \item[\texttt{AdaptiveTuningFunctionId}] (Chapter~\ref{ch:tuning}). Registered adaptive tuning functions taking harmonic context as input. \item[\texttt{TuningFileFormat}] (Chapter~\ref{ch:tuning}). Recognized external tuning file formats (Scala, MIDI Tuning Standard, additional formats via registry). \item[\texttt{CompatibilityMapping}] (Chapter~\ref{ch:tuning}). Declared compatibility between pitch spaces and tuning systems whose underlying spaces differ. \end{description} \section{Glyph and Font Extensions} \label{sec:ext:glyphs} \begin{description} \item[\texttt{CustomGlyphId}] (Chapter~\ref{ch:tuning}). Custom glyphs not in SMuFL, defined per score or per plugin. \item[\texttt{Composite} glyph references] (Chapter~\ref{ch:tuning}). Composite glyphs constructed from multiple primary glyph references. \item[\texttt{ModificationRegistryId}] (Chapter~\ref{ch:tuning}). Registered pitch-space modifications for grammar-defined accidentals. \end{description} \section{Score Graph Extensions} \label{sec:ext:graph} \begin{description} \item[\texttt{ExtensionOp}] (Chapter~\ref{ch:semops}). Implementation-defined operation kinds extending the core's semantic operations. Extension operations \MUST{} satisfy the operation framework's requirements (preconditions, effects, inverse, reduction rule, re-anchoring). \item[\texttt{CustomMarkerId}] (Chapter~\ref{ch:graph}). Custom marker kinds beyond the core's standard catalog. \item[\texttt{CustomAnalyticalKindId}] (Chapter~\ref{ch:graph}). Custom analytical annotation kinds. \item[\texttt{Custom event variants}] (Chapter~\ref{ch:graph}). The event taxonomy is closed in the core, but the \texttt{Group} layout object and the \texttt{Registered} fields throughout permit composition of core types into extended structures. \end{description} \section{Layout IR Extensions} \label{sec:ext:layout} \begin{description} \item[\texttt{SynthesisRegistryId}] (Chapter~\ref{ch:layout-ir}). Custom synthesis kinds for engraver-generated objects. \item[\texttt{ConstraintRegistryId}] (Chapter~\ref{ch:layout-ir}). Registered layout constraint kinds extending the standard constraint families. \item[\texttt{MappingRegistryId}] (Chapter~\ref{ch:graph}). Custom parameter mappings for graphic-object time bindings. \item[\texttt{Custom view kinds}] (Chapter~\ref{ch:graph}). The \texttt{ViewKind::Custom} variant admits user-defined view kinds. \end{description} \section{Constraint-Solver Extensions} \label{sec:ext:solver} \begin{description} \item[\texttt{LayoutConstraint::Registered}] (Chapter~\ref{ch:layout-ir}). Custom constraint families consumed by the solver. Extension constraints \MUST{} declare their strength. When soft, extension constraints contribute to the quality vector through an extension metric subject to the \texttt{NormalizedMetric} discipline (Chapter~\ref{ch:solver}) and to the tier-specific reference- suite thresholds. \end{description} \section{File Format Extensions} \label{sec:ext:format} \begin{description} \item[\texttt{FormatProfile::Custom}] (Chapter~\ref{ch:format}). Profile definitions beyond Full, ReadOnly, and Lite. \item[\texttt{Self-describing extension envelope}] (Chapter~\ref{ch:format}). Plugin-contributed and forward-compatible content is carried in self-describing extension records that the core preserves opaquely. \end{description} \section{Extension Registry Contract} \label{sec:ext:registry} Every extension registry shares a common structural contract. \begin{lstlisting}[language=Rust] pub trait ExtensionRegistry { type Id; type Definition; /// Resolve an identifier to its definition. fn resolve(&self, id: Self::Id) -> Option<&Self::Definition>; /// Enumerate all registered definitions. fn enumerate(&self) -> Vec<&Self::Definition>; /// Version of the registry. Score files declare which version /// their references target. fn version(&self) -> RegistryVersion; } \end{lstlisting} \begin{requirement} Every extension registry \MUST{} be versioned. Stored references to registered definitions \MUST{} include the registry version they target. Loading a score with references to registry versions newer than the reader understands \MUST{} preserve those references per the forward-compatibility rules of Chapter~\ref{ch:format}. \end{requirement} \subsection{Identifier Stability} \begin{requirement} Registry identifiers \MUST{} be stable across registry versions within a major version. Renumbering, reassignment, or repurposing of identifiers is forbidden within a major version. Removal of a definition \MUST{} reserve its identifier; the identifier \MUSTNOT{} be reassigned to a different definition. \end{requirement} \subsection{Discoverability} Registries \MUST{} support enumeration: an implementation can list all known definitions and their identifiers. This enables tooling (plugin browsers, score migration assistants, conformance testers) to operate against unknown registries by inspection. \section{Plugin Runtime Considerations} \label{sec:ext:runtime} The plugin runtime is outside this specification, but extension points are designed to admit a capability-bound runtime as described in the feature compendium: \begin{itemize} \item Extensions are isolatable: an extension's failure does not crash the host. \item Extensions are introspectable: their declared capabilities and inputs are inspectable before execution. \item Extensions are revocable: an extension can be disabled without invalidating the scores that reference it. \end{itemize} \begin{requirement} Extension points \MUST{} be designed such that disabling an extension does not invalidate stored content referencing it. The content remains in the score, opaque to the host, until the extension is re-enabled or the references are explicitly removed by the user. \end{requirement} \section{Conformance and Extensions} \label{sec:ext:conformance} Extensions do not affect core conformance directly, but they interact with it: \begin{requirement} A conforming implementation: \begin{itemize} \item \MUST{} support every extension point as a structural slot (i.e., scores containing extension references are readable and writable). \item \MUSTNOT{} require any specific extension to be installed for core conformance. \item \MAY{} require specific extensions for specific features (e.g., a particular plugin for a particular grammar). \item \MUST{} clearly distinguish between core conformance and extension-dependent features in its documentation. \end{itemize} \end{requirement} \section{Forward References} \begin{itemize} \item The plugin runtime (WebAssembly-based, capability-bound) is specified in the Plugin Runtime specification, outside this document. \item The complete extension registry catalog is delivered as a companion document. \item Implementation-defined extension contributions are governed by the implementation's published extension specification. \end{itemize} % =========================================================================== \appendix \chapter{Intentionally Deferred Types and Specifications} \label{app:deferred} This appendix catalogs types, concepts, and specifications that the core specification refers to but does not fully define. Each entry is \emph{deliberately external}: it is the responsibility of a named companion specification or a future revision of this document. The purpose of this appendix is to distinguish deliberately external content from accidentally missing content, so that reviewers and implementers can quickly tell which unresolved references represent open work and which represent boundaries to other documents. \section{Companion Specifications} \label{sec:deferred:companions} The following are companion specifications referenced throughout the core. None has been delivered as of this revision; each is named here with the chapter(s) that depend on it. \begin{description} \item[Binary Format companion.] Byte-level encoding of all canonical chunks: varint conventions, exact field widths, record layouts, endianness rules, string encoding details. Referenced from Chapter~\ref{ch:format}. The on-disk representations specified in that chapter are structural; the Binary Format specifies how the bits are laid out. \item[Text Projection companion.] The canonical s-expression text projection of the bundle: grammar, deterministic serialization rules, base64 encoding for opaque payloads, handling of extension data, round-trip semantics with binary form. Referenced from Chapter~\ref{ch:format} Section~\ref{sec:format:textproj}. \item[Profile Conformance specification.] The complete catalog of file-format profiles (Full, ReadOnly, Lite, plus any normative custom profiles), with their constraints, permitted compression algorithms, maximum block sizes, required extensions, and conformance test discipline. Referenced from Chapter~\ref{ch:format} Section~\ref{sec:format:profiles}. \item[Quality Metric Catalog.] Per-metric normalization functions mapping raw measurements to \texttt{NormalizedMetric} values, default tie-breaking weights, per-tier metric thresholds, and the formal definition of each quality metric in the normative metric set. Referenced from Chapter~\ref{ch:solver}. \item[Reference Suite.] The collection of test scores against which solver conformance is established, with per-tier inclusion, per-tier metric thresholds, and any fixed-expectation tests. Versioned with this specification. Referenced from Chapter~\ref{ch:solver} Section~\ref{sec:solver:conformance}. \item[Performance Reference Suite.] The collection of edit traces and reference scores against which performance conformance is established. Referenced from Chapter~\ref{ch:perf}. \item[Operation Catalog companion.] The complete list of primitive operations and compound operations supported by the core, each with full preconditions, effects, inverse, and reduction rule. The representative operations in Chapter~\ref{ch:semops} illustrate the contract; the full catalog is delivered separately. \item[Reference Algorithm companion.] A non-normative description of one starting-point implementation of the constraint solver. Explanatory and diagnostic only; conforming implementations are not required to use it. Referenced from Chapter~\ref{ch:solver} Section~\ref{sec:solver:reference-algo}. \end{description} \section{Externally Provided Components} \label{sec:deferred:external} The following are real components on which the core depends but which are owned by other specifications outside the Epiphany core scope. \begin{description} \item[SMuFL glyph catalog.] The Standard Music Font Layout provides the normative glyph repertoire. Referenced from Chapter~\ref{ch:tuning} and the layout chapters. SMuFL is maintained by the W3C Music Notation Community Group; the core spec declares its dependency on the SMuFL version targeted but does not redefine SMuFL. \item[Font metric provider.] Implementations resolve glyph geometric data (bounding boxes, anchor points, advance widths) through a font metric provider. The provider's interface is part of the implementation contract; specific font metrics are external content. \item[Audio engine specification.] Out of scope here per Chapter~\ref{ch:intro}. The core exposes data structures designed for allocation-free, lock-free audio-thread access (Chapter~\ref{ch:perf}); the audio engine's internals, latency obligations, and stream discipline are specified separately. \item[Plugin runtime specification.] A capability-bound, sandboxed runtime hosts extension code. The runtime is out of scope here; the extension points (Chapter~\ref{ch:extension}) are designed to admit such a runtime. \item[Collaboration transport.] The wire protocol by which operation envelopes are exchanged between replicas is out of scope here. Chapter~\ref{ch:semops} specifies what must be exchanged and how it is reduced; the transport layer is separate. \item[User interface and editor.] Editor commands, selection semantics, keyboard shortcuts, gesture grammar, and UI-level interaction patterns are out of scope. The core exposes a programmatic operation API; UI mapping is layered above. \end{description} \section{Open Canonical Algorithms} \label{sec:deferred:open-algos} The following algorithms affect canonical state but are not yet specified normatively, profile-declared, or marked non-canonical. Per Appendix~\ref{app:determinism} Section~\ref{sec:det:open}, each \MUST{} receive one of those dispositions before the document can claim full canonical score determinism across implementations. \begin{description} \item[Spelling pre-pass algorithm.] The algorithm that computes default spellings from key signature, harmonic context, and notation grammar. Referenced from Chapter~\ref{ch:pitch}. Several established algorithms exist (Longuet-Higgins, Temperley pitch-spelling preference rules, and others); the choice of normative algorithm or profile-declared algorithm set is open. \item[Notational decomposition algorithm.] The algorithm that breaks a sounding duration into notehead values, augmentation dots, and ties under metric context. Referenced from Chapter~\ref{ch:time}. Conservatoire engraving manuals describe the conventions verbosely; the formalization is open. \item[Tempo curve integration.] The numerical integration algorithm for converting musical position to wall-clock position under \texttt{TempoShape::Curve}. Referenced from Chapter~\ref{ch:time}. Possible candidates include Romberg integration and adaptive Gauss-Kronrod; the normative choice is open. \item[Wallclock-to-musical root-finding.] The root-finding algorithm for the inverse of tempo curve integration. Referenced from Chapter~\ref{ch:time}. Possible candidates include bisection with monotonicity guarantees and Brent's method; the normative choice is open. \end{description} \section{Extension Registry Catalogs} \label{sec:deferred:registries} The extension registries enumerated in Chapter~\ref{ch:extension} (notation grammar extensions, tuning system extensions, glyph and font extensions, score graph extensions, layout IR extensions, solver extensions, file format extensions) are referenced by typed registry identifiers in the core. The concrete catalogs of registered extensions (the contents of each registry) are delivered as separate, versioned registry documents. Each registry document specifies its identifier numbering, the version under which each registered identifier was added, and the registered content's contract. \section{Support-Type Identity Semantics} \label{sec:deferred:support-types} Several identifier-like support types appear throughout the specification by name and shape, but their detailed encoding is delivered by companion specifications. Their semantic identity rules are normative here. \begin{longtable}{p{3.4cm} p{3.6cm} p{6.6cm}} \toprule \textbf{Type} & \textbf{Source} & \textbf{Semantic rule} \\ \midrule \endhead \texttt{FileUuid} & 128-bit random at bundle creation & Physical bundle identity. Fixed for the lifetime of the physical bundle; preserved by filesystem byte-copy; freshly minted on Save As, export-as-new-bundle, and derivative-work creation. \\ \texttt{DocumentId} & 128-bit random at logical-work creation & Logical work identity. Stable across Save As copies intended to remain versions of the same work. May persist across forks at user discretion. \\ \texttt{LineageId} & 128-bit random at fork-with-ancestry & Shared-ancestor identity. Optional. Multiple documents may share a \texttt{LineageId} declaring common ancestry for version-control or genealogy purposes. \\ \texttt{ManifestId} & Content-derived from manifest payload & Unique per manifest version. Derived as \texttt{trunc128(BLAKE3("MUSCMNIF" || document\_id || generation || manifest\_body))} with the \texttt{manifest\_id} field excluded from \texttt{manifest\_body}; the derivation is ratified normatively in Section~\ref{req:format:manifest-id}. \\ \texttt{SnapshotId} & Content-derived from snapshot payload and frontier & Stable for a snapshot's root and causal frontier. Two snapshots with identical materialized state at identical frontiers have identical ids. The Binary Format companion defines the exact derivation. \\ \texttt{BlobId} & BLAKE3 of blob content with domain tag \texttt{"MUSCBLOB"} & Content hash of the blob's uncompressed payload, identical to the blob's \texttt{ContentHash}. \\ \texttt{AuthorId} & Implementation-defined; persists across replica reseatings & Identifies the human author of operations, distinct from \texttt{ReplicaId} (which identifies the device or session). One author may operate multiple replicas; one replica may host one author at a time. The collaboration transport companion defines authentication. \\ \texttt{ReductionAlgorithmVersion} & Semantic version & Selects canonical reduction semantics. Two replicas with different \texttt{ReductionAlgorithmVersion} may produce different canonical states from the same operation set; the active superblock declares the version under which the bundle's canonical base was materialized (Section~\ref{sec:format:bundle}). \\ \texttt{SolverProfile} & Registered profile identifier & Selects the solver's hard-constraint set, normalized-metric thresholds, tie-breaking weights, and active extension catalog (Chapter~\ref{ch:solver}). The Quality Metric Catalog companion defines the registered profile catalog. \\ \texttt{ProfileId} (file format) & Registered profile identifier & Selects the file-format conformance subset (Full, ReadOnly, Lite, or registered Custom). The Profile Conformance companion defines the registered profile catalog. \\ \texttt{ContentHash} & BLAKE3-256 output (32 bytes) & Cryptographic content hash. The canonical hash for all content-addressed objects in the bundle. \\ \texttt{ChunkId} & Newtype around \texttt{ContentHash} & Same 32 bytes as the underlying \texttt{ContentHash}; the type distinction makes role visible at use sites. \\ \texttt{OperationId} & 64-bit \texttt{ReplicaId} + 64-bit counter, both randomly initialized; counter monotonic & Stable identity of an operation, set by the authoring replica at the moment of authoring. Never reassigned. \\ \texttt{TransactionId} & 128-bit, author-minted & Identifies a transaction declared by a \texttt{DeclareTransaction} envelope. Member envelopes reference this id in their envelope \texttt{transaction} field. \\ \texttt{ConflictId} & Content-derived via BLAKE3 truncation with domain tag \texttt{"MUSCCONF"} & Identifier of a conflict record. Derived from canonical kind, sorted causing operations, sorted affected objects. Section~\ref{sec:semops:conflict-id}. \\ \texttt{Typed identifiers} (EventId, PitchId, VoiceId, StaffId, etc.) & 64-bit \texttt{ReplicaId} + 64-bit counter; or \texttt{ReplicaId::SYSTEM\_DERIVED} + BLAKE3-derived counter for system-derived identifiers & Stable identity within their typed namespace. Section~\ref{sec:graph:system-derived}. \\ \bottomrule \end{longtable} \begin{requirement} Implementations \MUST{} respect the semantic identity rules declared above. They \MAY{} freely choose the bit-level encoding of each type (the Binary Format companion specifies the canonical wire encoding); they \MAY NOT{} reinterpret the identity semantics. In particular, two distinct works \MUSTNOT{} share a \texttt{DocumentId}; two byte-distinct chunks \MUSTNOT{} share a \texttt{ContentHash}; two distinct operations \MUSTNOT{} share an \texttt{OperationId}. \end{requirement} \section{Distinction from Open Questions} \label{sec:deferred:vs-open} This appendix lists content that is \emph{deliberately external} to the core specification: it is owned by a companion document, a future revision, or a third-party specification. It does \emph{not} list ``open questions'' in the core's own design space. Those are marked with the \textsf{\textit{Open Question}} callout where they appear in the body of the text, and they represent unresolved decisions \emph{within} the core specification's scope. The two categories overlap only at the four open canonical algorithms in Section~\ref{sec:deferred:open-algos}, which are unresolved in the core text \emph{and} whose resolution will likely take the form of a companion specification or a profile declaration. \chapter{Glossary} \label{app:glossary} This glossary defines key terms used throughout the specification. Definitions are drawn from their introducing chapters; the chapter reference points to the authoritative definition. \begin{description} \item[Acoustic pitch] (Chapter~\ref{ch:pitch}). The frequency realization of a pitch under an active tuning system. One of the two intrinsic layers of a \texttt{Pitch}. \item[Adaptive tuning] (Chapter~\ref{ch:tuning}). A tuning system whose frequency resolution depends on harmonic context, not on pitch-space position alone. \item[Aleatoric time] (Chapter~\ref{ch:time}). A region time model in which events have partial or undefined ordering, expressed as a directed acyclic graph with optional interval bounds. \item[Anchor] (Chapter~\ref{ch:time}). A reference to a point in time, expressed relative to an identified object (event, measure, region, wall-clock time) plus an offset. Anchors survive edits that do not delete their target. \item[Arena (event arena)] (Chapter~\ref{ch:graph}). The flat storage owned by a score holding all events of all kinds, with $O(1)$ lookup by \texttt{EventId}. \item[Bundle] (Chapter~\ref{ch:format}). The on-disk container holding a score: header, manifest, chunk store, and blob store in a single \texttt{.musc} file. \item[Canonical reduction] (Chapter~\ref{ch:semops}). The deterministic procedure that materializes a score graph from the operation set: sort by canonical order, group transactions, apply each operation against working state, record effects. \item[Canvas] (Chapter~\ref{ch:graph}). The spatial root of a score: a 2D space partitioned into regions of staff-based, free-graphic, or hybrid content. \item[Causal predecessors] (Chapter~\ref{ch:semops}). The set of operations on which a given operation causally depends. Used by the CRDT layer to determine concurrency. Expressed in canonical form as a dotted version vector (\texttt{CausalContext}); see ``Dotted version vector.'' \item[Chunk] (Chapter~\ref{ch:format}). An immutable content-addressed unit of storage within a bundle. Identified by the cryptographic hash of its uncompressed payload, with a domain-separated preimage. \item[Compound operation] (Chapter~\ref{ch:semops}). A user-facing operation that expands into a sequence of primitive operations applied as a transaction. \item[Conflict record] (Chapter~\ref{ch:semops}). A first-class graph object recording an operation that could not apply cleanly under canonical reduction. Stable, addressable, user-visible, and resolvable by subsequent \texttt{ResolveConflict} operations. \item[CRDT] (Chapters~\ref{ch:semops},~\ref{ch:format}). Conflict-free replicated data type. In Epiphany, the \emph{operation set} is a true CRDT (a grow-only set of operation envelopes); the materialized score graph is \emph{not} itself a CRDT but is the deterministic reduction of that set. \item[Cross-cutting structure] (Chapter~\ref{ch:graph}). A first-class object spanning multiple tree-nodes via references: slurs, ties, beams, spanners, markers, repeats, analytical annotations, comments, graphic gestures, lyrics, chord symbols. \item[Decomposition] (Chapter~\ref{ch:time}). The notational breakdown of an event's sounding duration into notehead values, augmentation dots, and ties. Attached externally by the decomposition pre-pass. \item[Delta] (Chapter~\ref{ch:semops}). The structural record of an operation's effect, sufficient to reconstruct the pre-state of every object the operation modifies. \item[Document identifier] (Chapter~\ref{ch:format}). The logical work identity (\texttt{DocumentId}), stable across Save As copies that are intended to remain versions of the same work. May persist across forks at the user's discretion. Distinct from \texttt{FileUuid} (physical bundle identity, changes on Save As) and \texttt{LineageId} (optional shared-ancestor identity). \item[Dotted version vector] (Chapters~\ref{ch:semops}, \ref{ch:format}). The compact causal-context representation of operation history. Operational semantics (causal precedence, frontier coverage, monotonic growth) are specified in Chapter~\ref{ch:semops}; the on-disk serialization is specified in Chapter~\ref{ch:format}. \item[Edit barrier] (Chapter~\ref{ch:format}). A structured declaration by an extension that certain edits would invalidate the extension's data. Implementations not understanding the extension refuse matching edits unless the user performs an explicit unsafe edit. \item[Engraving decision] (Chapter~\ref{ch:layout-ir}). A choice made by the engraver (stem direction, beam consolidation, etc.) recorded explicitly in the layout IR for inspection and override. \item[Engraving override] (Chapter~\ref{ch:layout-ir}). A user assertion that the engraver's default decision should be replaced. Authoritative in the score graph; projected into the layout IR. \item[Event] (Chapter~\ref{ch:graph}). A rhythmic atom of the score: pitched, unpitched, rest, indeterminate, trajectory, graphic, or cue. Owned by exactly one voice. \item[Format profile] (Chapter~\ref{ch:format}). A named subset of the file format that conforming implementations promise to support. Profiles enumerated: Full, ReadOnly, Lite, Custom. \item[Frame budget] (Chapter~\ref{ch:perf}). The time allowed for a layout-and-render operation to maintain 60\,Hz (16.7\,ms) or 120\,Hz (8.3\,ms) interactivity. \item[Glyph catalog identity] (Chapter~\ref{ch:layout-ir}). The identifier (SMuFL version, font id, optional font version, metrics hash) under which layout output was produced. Required for any byte-equal-output layout conformance claim. \item[Graphic content] (Chapter~\ref{ch:graph}). Free-form vector graphics, drawn strokes, text, and images within free-graphic or hybrid regions. \item[Hard constraint] (Chapter~\ref{ch:solver}). A constraint that must be satisfied; violation produces a solver error. \item[Hybrid logical clock] (Chapters~\ref{ch:semops}, \ref{ch:format}). A clock combining physical wall-clock time with a logical counter, used as a tie-breaker in canonical reduction order. Operational semantics (per-replica monotonicity, role in canonical reduction ordering, malformed-envelope behavior) are specified in Chapter~\ref{ch:semops}; the on-disk serialization is specified in Chapter~\ref{ch:format}. \item[Identifier (typed)] (Chapter~\ref{ch:graph}). A 128-bit stable identifier for a named object: \texttt{EventId}, \texttt{VoiceId}, \texttt{StaffId}, etc. Generated by replica-plus-counter scheme. \item[Incremental layout] (Chapter~\ref{ch:layout-ir}). Re-layout of only the IR objects whose dependencies have changed, with cached values reused elsewhere. \item[Instrument] (Chapter~\ref{ch:graph}). An abstract definition of a sounding entity: name, transposition, range, default sound, default clef, default staff count. \item[Layout IR] (Chapter~\ref{ch:layout-ir}). The pipeline of intermediate representations between score graph and renderer: \texttt{LogicalLayoutIR}, \texttt{ConstrainedLayoutIR}, \texttt{ResolvedLayoutIR}, \texttt{RenderIR}. \item[Manifest] (Chapter~\ref{ch:format}). The mutable index of a bundle: a table of operation roots, optional canonical base snapshot, acceleration snapshots, blob roots, profile declarations, and extension declarations. Itself a content-addressed chunk; the active manifest is selected via the active superblock slot (Chapter~\ref{ch:format}). \item[Metric time] (Chapter~\ref{ch:time}). A region time model with measures, beats, and exact rational positions. The dominant model for Western notated music. \item[Notation grammar] (Chapter~\ref{ch:tuning}). The declarative definition of a notation system's pitch space, intervals, accidentals, and spelling rules. CMN is the default; others extend. \item[Operation envelope] (Chapter~\ref{ch:semops}). A transmitted, stored, and reducible record carrying an operation's payload together with its identity, ordering stamp, causal context, and optional transaction grouping. The unit of replication. \item[Operation kind] (Chapter~\ref{ch:semops}). The tagged enumeration of primitive operation variants: InsertEvent, DeleteEvent, RespellPitch, Transpose, ChangeRegionTimeModel, and so on, plus \texttt{Registered} for extension-defined primitives. The full catalog is delivered separately. \item[Operation log] (Chapter~\ref{ch:format}). The physical storage of the canonical operation-envelope set on disk, as operation-envelope blocks. The canonical document is the deterministic reduction of this envelope set, or, after pruning, of the manifest's \texttt{canonical\_base} snapshot plus envelopes whose \texttt{OperationId}s are not covered by the base's causal frontier. \item[Operation set] (Chapter~\ref{ch:semops}). The replicated grow-only CRDT of operation envelopes. The canonical document is its deterministic reduction. \item[Pareto frontier (design target)] (Chapter~\ref{ch:solver}). The set of layouts for which no quality metric can be improved without worsening another. Solvers \SHOULD{} seek layouts near the frontier; conformance is reference-suite-based, not proof of Pareto optimality. \item[Part] (Chapter~\ref{ch:graph}). A per-instrument or per-section view onto the score for printed extraction. Parts are projections, not storage. \item[Pitch space] (Chapter~\ref{ch:tuning}). The analytical universe of a pitch system: what positions exist, how they relate, what accidentals are valid. Distinct from tuning system. \item[Pitch spelling] (Chapter~\ref{ch:pitch}). The notational appearance of a pitch: nominal, accidental, octave. Attached externally by the spelling pre-pass. \item[Primitive operation] (Chapter~\ref{ch:semops}). The atomic unit of editing. Primitive operations compose into compound operations and transactions. \item[Provenance] (Chapter~\ref{ch:layout-ir}). The record on each layout IR object tracing back to its score graph source, enabling selection, editing back-references, and incremental layout invalidation. \item[Proportional time] (Chapter~\ref{ch:time}). A region time model in which horizontal position is wall-clock time, with no measures or beats. \item[Re-anchoring] (Chapter~\ref{ch:semops}). The normative response of a cross-cutting structure when its referent is deleted: cascade delete, truncate, re-anchor, mark orphaned, or refuse. \item[Reference pitch] (Chapter~\ref{ch:tuning}). The anchor converting tuning system structure to absolute frequencies: a position and a frequency in Hertz. A property of the score, not the tuning system. \item[Region] (Chapter~\ref{ch:graph}). A spatial-temporal container in the canvas declaring a time model (metric, proportional, aleatoric) and a content model (staff-based, free-graphic, hybrid). \item[Repair record] (Chapter~\ref{ch:semops}). A deterministic compensating change made during reduction to preserve graph invariants (re-anchoring, spanner truncation, attachment tombstoning, voice promotion, tuplet compensation). Recorded as part of canonical state via \texttt{OperationEffect::AppliedWithRepair}. \item[Replica] (Chapter~\ref{ch:graph},~\ref{ch:format}). An instance of a score on a device. Each replica has a unique identifier and authors operations with monotonic local sequence numbers. The replica identifier \texttt{ReplicaId::SYSTEM\_DERIVED} is reserved for deterministically-derived system identifiers. \item[Retention policy] (Chapter~\ref{ch:format}). The declared rule governing how many old manifests (and their reachable chunks) are preserved in the bundle for rollback purposes. Rollback is achieved through retained manifests in the chunk store, not by retaining multiple on-disk superblock generations. \item[Scale position] (Chapter~\ref{ch:pitch}). The analytical identity of a pitch within a pitch space. One of the two intrinsic layers of a \texttt{Pitch}. \item[Score graph] (Chapter~\ref{ch:graph}). The in-memory representation of all musical content in a score: canonical truth from which layout, serialization, and editing operations derive. \item[Semantic operation] (Chapter~\ref{ch:semops}). A mutation of the score graph: insert event, delete measure, transpose, respell pitch, etc. \item[SMuFL] (Chapter~\ref{ch:tuning}). Standard Music Font Layout. The normative source for glyph references in the core. \item[Snapshot] (Chapter~\ref{ch:format}). A materialized score state. As an acceleration snapshot (listed in \texttt{acceleration\_snapshots}), derivative and discardable. As a canonical-base snapshot (named in the manifest's \texttt{canonical\_base} field with declared causal frontier and reduction algorithm version), the canonical base for pruned documents, in which case the canonical state is the base snapshot plus envelopes whose \texttt{OperationId}s are not covered by the base's causal frontier. \item[Soft constraint] (Chapter~\ref{ch:solver}). A constraint with a weight; violation is permitted but penalized in quality metrics. \item[Sounding duration] (Chapter~\ref{ch:time}). The exact rational duration an event occupies in time. Intrinsic; notational decomposition is attached separately. \item[Spring slot] (Chapter~\ref{ch:layout-ir}). A time slot's horizontal spacing parameters (min, preferred, max widths; stretch, compress factors) consumed by the constraint solver. \item[Staff] (Chapter~\ref{ch:graph}). The global, abstract staff identity persisting across the entire score. A staff is declared once at the score level and referenced by staff instances in any region where it appears. \item[Staff instance] (Chapter~\ref{ch:graph}). The region-local manifestation of a staff: the voices, measures, clef changes, key changes, and metric grid that belong to that staff for one region. A single \texttt{Staff} may have multiple \texttt{StaffInstance}s across the score. \item[Tempo map] (Chapter~\ref{ch:time}). The function mapping musical positions to wall-clock positions, piecewise over musical time with constant, linear, exponential, or curve segments. \item[Time anchor] (Chapter~\ref{ch:time}). See ``Anchor.'' \item[Time axis] (Chapter~\ref{ch:layout-ir}). The polymorphic layout-IR component projecting time positions to horizontal positions. Three implementations: metric, proportional, aleatoric. \item[Transaction] (Chapter~\ref{ch:semops}). A replicated operation group that reduces atomically: either every member applies and the transaction reduces to \texttt{Applied}, or the entire transaction reduces to \texttt{Conflicted} with a recorded conflict. \item[Trajectory] (Chapter~\ref{ch:graph}). A continuous pitch-motion event: glissando, portamento, pitch bend. A sibling to pitched events under the event taxonomy. \item[Tuning system] (Chapter~\ref{ch:tuning}). The mechanism mapping pitch-space positions to frequencies, given a reference pitch. Distinct from pitch space. \item[Tuplet] (Chapter~\ref{ch:time}). A grouping object annotating irregular rhythmic groupings. Does not modify sounding durations; purely notational and analytical. \item[Typed object identifier] (Chapter~\ref{ch:graph}). A tagged union (\texttt{TypedObjectId}) over every identifier kind in the score graph. Used wherever an object is referenced generically (cross-cutting endpoints, conflict \texttt{affected\_objects}, repair records, edit barriers). \item[Validation mode] (Chapter~\ref{ch:semops}). The strictness setting for operation precondition checking: strict authoring or lenient replay. \item[Vertical band] (Chapter~\ref{ch:layout-ir}). A horizontal slice of the canvas with spring parameters for vertical spacing: staff bands, inter-staff gaps, inter-system gaps, margin bands. \item[View] (Chapter~\ref{ch:graph}). A recipe for displaying a configuration of the score: which layers are active, which parts are rendered, what overrides apply. \item[Voice] (Chapter~\ref{ch:graph}). A polyphonic line within a staff. Owns a sequence of events. \item[Wall-clock time] (Chapter~\ref{ch:time}). Time measured by the playback engine or external sync source, in fixed-point nanoseconds. Distinct from musical time. \end{description} \chapter{Bibliography and References} \label{app:refs} \section*{Engraving Treatises} \begin{description} \item[Behind Bars.] Elaine Gould. \emph{Behind Bars: The Definitive Guide to Music Notation.} Faber Music, 2011. The principal modern reference for music engraving conventions. \item[Music Notation in the Twentieth Century.] Kurt Stone. \emph{Music Notation in the Twentieth Century: A Practical Guidebook.} W. W. Norton, 1980. Authoritative reference for contemporary notation. \item[Essentials of Music Notation.] Tom Ross. \emph{The Art of Music Engraving and Processing.} Hansen Books, 1970. Classical treatise on engraving conventions, still influential. \item[Music Notation.] Gardner Read. \emph{Music Notation: A Manual of Modern Practice.} Crescendo Publishers, 1969. Reference for engraving practices and history. \end{description} \section*{Music Theory and Analysis} \begin{description} \item[The Cognition of Basic Musical Structures.] David Temperley. MIT Press, 2001. Source of preference-rule approaches to spelling and meter analysis. \item[Pitch Spelling and the Line of Fifths.] H. Christopher Longuet-Higgins. Foundational work on pitch-spelling algorithms. \item[Tonal Pitch Space.] Fred Lerdahl. Oxford University Press, 2001. Pitch-space theory as analytical framework. \item[A Generative Theory of Tonal Music.] Fred Lerdahl and Ray Jackendoff. MIT Press, 1983. Foundational work in formal music theory and the source of preference-rule methodology. \end{description} \section*{Tuning, Microtonality, and Just Intonation} \begin{description} \item[Genesis of a Music.] Harry Partch. University of Wisconsin Press, 2nd ed. 1974. Foundational text in extended just intonation and microtonal practice. \item[Tuning, Timbre, Spectrum, Scale.] William Sethares. Springer, 2nd ed. 2004. Modern treatment of tuning systems and their psychoacoustic foundations. \item[Helmholtz-Ellis JI Pitch Notation.] Marc Sabat and Wolfgang von Schweinitz. The HEJI accidental system referenced by the core's built-in JI pitch spaces. \item[Sagittal: A Microtonal Notation System.] George D. Secor and David C. Keenan. The Sagittal microtonal accidental system. \end{description} \section*{Constraint-Based Layout} \begin{description} \item[Cassowary Linear-Arithmetic Constraint Solver.] Greg J. Badros, Alan Borning, and Peter J. Stuckey. \emph{The Cassowary Linear Arithmetic Constraint Solving Algorithm.} ACM Transactions on Computer-Human Interaction, 8(4):267-306, 2001. Source of the constraint-solver model referenced in Chapter~\ref{ch:solver}. \item[Music Engraving with Constraint Programming.] Multiple authors. Survey of constraint-based approaches to music engraving. \item[The Gourlay Algorithm.] J. S. Gourlay. \emph{A Language for Music Printing.} Communications of the ACM, 1986. Source of the proportional spacing model referenced in Chapter~\ref{ch:layout-ir}. \end{description} \section*{CRDT and Distributed Systems} \begin{description} \item[A Comprehensive Study of Convergent and Commutative Replicated Data Types.] Marc Shapiro, Nuno Pregui\c{c}a, Carlos Baquero, and Marek Zawirski. INRIA Technical Report RR-7506, 2011. Foundational survey of CRDTs. \item[Dotted Version Vectors.] Nuno Pregui\c{c}a et al. Refinement of vector clocks used in the file format (Chapter~\ref{ch:format}). \item[Hybrid Logical Clocks.] Sandeep S. Kulkarni et al. Clocks combining physical and logical time, used for total ordering of operation envelopes (Chapter~\ref{ch:semops}). \end{description} \section*{Prior-Art Notation Formats} \begin{description} \item[MusicXML.] Recordare LLC, later W3C Music Notation Community Group. The dominant interchange format; surveyed for the interchange profile work referenced in Chapter~\ref{ch:graph}. \item[MEI (Music Encoding Initiative).] An XML-based encoding designed for scholarly editions and musicology. Influenced the spec's handling of editorial apparatus and analytical layers. \item[MNX.] W3C Music Notation Community Group draft. The successor effort to MusicXML; informed several decisions on layered data models. \item[LilyPond.] David Bauer, Han-Wen Nienhuys, Jan Nieuwenhuizen. Open-source music engraving software using a text-based input format and a sophisticated rule-based engraver. Significant influence on the spec's engraving philosophy. \item[SMuFL.] W3C Music Notation Community Group. The Standard Music Font Layout, referenced as the normative glyph catalog in Chapter~\ref{ch:tuning}. \end{description} \section*{Additional References} \begin{description} \item[The Anatomy of a Note.] Multiple papers on the data representation of musical notation in computer systems. \item[Notation in Computer Music.] Various proceedings of the International Computer Music Conference (ICMC) and the New Interfaces for Musical Expression (NIME) workshops. \end{description} \chapter{Determinism Contract} \label{app:determinism} This appendix consolidates the reproducibility obligations that appear throughout the specification. It defines exactly which values are admissible in canonical state, how they are serialized, how comparisons work, and where implementation freedom begins. The appendix is the document's legal code for reproducibility: where the body of the specification says ``deterministic,'' this appendix says what that means. \section{Thesis} \label{sec:det:thesis} \begin{requirement} Canonical document state \MUST{} be independent of platform, CPU, locale, thread scheduling, hash-map iteration order, floating-point environment, compression settings, and wall-clock timing. Where exact equality is required, this specification defines canonical representations. Where numerical approximation is unavoidable, this specification defines deterministic tolerances, quantization, and reporting rules. Implementations \MAY{} freely choose internal data structures, algorithms, and parallelism strategies. The canonical externally observable result \MUSTNOT{} depend on those choices. \end{requirement} \section{Layers of Determinism} \label{sec:det:layers} The specification distinguishes five layers. Each layer has its own determinism contract; conflating them is the most common source of impossible requirements. \begin{description} \item[Canonical score determinism] (Chapter~\ref{ch:semops}). The reduction of an operation-envelope set to a materialized score graph is fully deterministic \emph{when every algorithm contributing to that reduction has received one of the dispositions in Section~\ref{sec:det:open}} (normatively specified, profile-declared by versioned identifier, or explicitly non-canonical). Under that condition, identical operation sets produce identical materialized score states across all conforming implementations, with identical object identifiers, tombstones, conflicts, and effects. For algorithms still in open-question state (Section~\ref{sec:det:open}), this byte-equal cross- implementation guarantee \MUSTNOT{} be claimed; the output of such algorithms is either non-canonical or governed by the profile's declared algorithm version. \item[Canonical serialization determinism] (Chapter~\ref{ch:format}). The bundle format's chunk hashes, manifest encoding, text projection, and the ordering of canonical maps and sets are deterministic. Two implementations producing the same canonical state \MUST{} produce identical chunk content hashes and identical text-projection bytes. \item[Layout determinism] (Chapter~\ref{ch:solver}). Within a single solver implementation at a fixed version, with fixed profile, fixed budget, and fixed font/glyph metrics, layout output is byte-identical across runs. Across different solver implementations, layout output \MAY{} differ aesthetically within reference-suite thresholds. \item[Conformance determinism]. Reference-suite-based: conforming implementations need not produce byte-identical output to each other; they need to fall within declared metric thresholds on every reference-suite entry. See Chapter~\ref{ch:solver} Section~\ref{sec:solver:conformance}. \item[Non-canonical cache determinism]. Implementations maintain caches (operation index, layout cache, glyph metric cache, render artifact cache). These \MAY{} vary across runs, platforms, and implementations. They \MUSTNOT{} affect canonical state and \MUST{} be discardable (Chapter~\ref{ch:semops} Section~\ref{sec:semops:caches}). \end{description} \begin{requirement} Byte-equality obligations \MUST{} be applied only to the layer for which they are stated. Implementations \MUSTNOT{} interpret ``deterministic'' more strongly than its layer specifies, and \MUSTNOT{} interpret it more weakly either. \end{requirement} \begin{rationale} The conditional in canonical score determinism is essential. Several algorithms affecting canonical state remain open questions in this draft: the spelling pre-pass (Chapter~\ref{ch:pitch}), the notational decomposition algorithm (Chapter~\ref{ch:time}), and the \texttt{wallclock\_to\_musical} root-finding algorithm (Chapter~\ref{ch:time}). Until those algorithms receive Section~\ref{sec:det:open} dispositions, cross-implementation byte equality cannot be promised for outputs derived from them. This is not a weakening of the determinism contract; it is a precise statement of what the contract currently covers. Once the open algorithms are specified or profile-declared, the conditional resolves and full cross-implementation byte equality applies to all of canonical state. \end{rationale} \section{Floating-Point Values in Canonical State} \label{sec:det:fp} \subsection{Permitted Forms} \begin{requirement} Canonical stored floating-point values \MUST{} be finite IEEE 754 binary64 values. NaN and infinity \MUSTNOT{} appear in canonical chunks: implementations \MUST{} reject them at serialization time and \MUST{} treat their presence on read as data corruption. The value \texttt{-0.0} (negative zero) \MUST{} be canonicalized to \texttt{+0.0} before storage. Two floating-point values that differ only in zero sign are equal under canonical comparison. Floating-point values appear in advisory, acoustic, tuning, tempo, layout, and quality-metric contexts. Where exact identity is needed (object identifiers, operation ordering, graph membership, hash identity), floating point \MUSTNOT{} be used. \end{requirement} \subsection{Serialization} \begin{requirement} Canonical \texttt{f64} values are serialized as little-endian IEEE 754 binary64 octets after applying the \texttt{-0.0 $\to$ +0.0} canonicalization. The eight serialized bytes are the canonical representation; two values whose canonical bytes are equal are canonically equal. Hash preimages incorporating floating-point values (Section~\ref{sec:format:hashing}) \MUST{} use the canonical serialized bytes, not platform-native byte orders or representations. \end{requirement} \subsection{Equality} \begin{requirement} Canonical equality of two floating-point values is byte equality of their canonical serialized representations. The IEEE 754 notion that \texttt{NaN $\ne$ NaN} is irrelevant in canonical state, because canonical state cannot contain NaN. For \texttt{NormalizedMetric} values (Chapter~\ref{ch:solver}), additional invariants apply: the value is finite, in $[0.0, 1.0]$, and lower is better. Comparisons of \texttt{NormalizedMetric} use IEEE 754 ordered comparison; the \texttt{-0.0 $\to$ +0.0} canonicalization applies before comparison. \end{requirement} \section{Rounding and CPU Behavior} \label{sec:det:rounding} \begin{requirement} Numerical operations whose results enter canonical state \MUST{} use IEEE 754 round-to-nearest, ties-to-even. The ambient floating-point rounding mode (which on most platforms defaults to round-to-nearest-ties-to-even but can be changed) \MUSTNOT{} affect canonical results: implementations \MUST{} either save/restore the rounding mode or use operations insensitive to it. Implementations \MUSTNOT{} compile canonical numerical algorithms with \texttt{-ffast-math}, \texttt{-funsafe-math- optimizations}, or equivalent flags that permit non-IEEE-conformant transformations. Implementations \MUSTNOT{} reorder, reassociate, or distribute floating-point expressions in ways that can change the canonical result. Implementations \MUSTNOT{} silently fuse multiplications and additions into fused-multiply-add operations unless the algorithm explicitly requests FMA. Implementations \MUSTNOT{} depend on, or be sensitive to, ambient floating-point exception flags or host rounding mode. Where implementations use SIMD, GPU acceleration, or platform math libraries (\texttt{libm}, vendor math libraries) in canonical algorithms, they \MUST{} produce results identical to a strict-IEEE single-threaded baseline. Differences in transcendental function implementations (\texttt{sin}, \texttt{cos}, \texttt{exp}, \texttt{log}) across libm versions are a real source of nondeterminism; implementations using transcendentals in canonical algorithms \MUST{} use a documented portable implementation (e.g., a vendored math library or a software fallback path). \end{requirement} \section{Exact and Quantized Representations} \label{sec:det:exact} The specification prefers exact representations over floating-point wherever possible. The following are exact by design: \begin{description} \item[Time and duration.] \texttt{MusicalPosition} and \texttt{MusicalDuration} are rational (Chapter~\ref{ch:time}). Inline-or-promoted representation; arithmetic is exact. \item[Operation ordering.] Causal order from DVV; total order by HLC and identifier (Chapter~\ref{ch:semops}). All integer- valued. \item[Identifiers.] All graph identifiers are 128-bit values composed of replica plus counter (Chapter~\ref{ch:graph}). \item[Hashes.] All canonical hashes are BLAKE3-256 (Chapter~\ref{ch:format}). \end{description} \subsection{Quantized Layout Coordinates} Spatial layout coordinates require numeric computation, but canonical output must be stable. The specification quantizes canonical coordinates to a fixed grid. \begin{lstlisting}[language=Rust] /// A canonical spatial coordinate, quantized to 1/1024 of a /// staff space. Used for canonical serialized output of /// ResolvedLayoutIR positions. Internal solvers MAY use floating /// point during computation; canonical serialization rounds to /// QuantizedCoord. pub struct QuantizedCoord { /// Coordinate value in units of 1/1024 staff space. pub units: i64, } \end{lstlisting} \begin{requirement} The canonical spatial coordinate grid is $1/1024$ staff space per unit. Layout coordinates emitted into canonical \texttt{ResolvedLayoutIR} \MUST{} be quantized to this grid before serialization. The quantization rule is round-to-nearest, ties-to-even on the integer unit value. Implementations \MAY{} use floating-point coordinates internally during layout computation. The act of quantization at serialization time absorbs all floating-point variation from the canonical output. Two implementations whose internal computations agree to better than $1/2048$ staff space at every coordinate produce identical canonical output after quantization. The grid spacing is fixed at $1/1024$ staff space for this format version. Future major versions \MAY{} adjust the spacing; doing so is a non-backward-compatible change. \end{requirement} \begin{rationale} $1/1024$ staff space is finer than visual resolution at any practical printing density (a staff space is typically 1.5--2\,mm, so $1/1024$ is roughly 1.5--2\,$\mu$m, well below print resolution). The grid is fine enough that quantization is inaudible musically and invisible visually, but coarse enough that floating-point noise from different solver implementations routinely rounds away. \end{rationale} \section{Tolerance Classes} \label{sec:det:tolerances} The specification refers to numerical tolerances in several places (acoustic comparison, layout quality thresholds, tempo integration, solver residual). All such tolerances belong to one of a small set of named classes, each with declared semantics. \begin{lstlisting}[language=Rust] pub enum ToleranceClass { /// Acoustic pitch comparison, in cents. AcousticCents, /// Layout coordinate comparison, in staff spaces. LayoutCoordinate, /// Quality metric comparison; absolute on the [0.0, 1.0] /// NormalizedMetric scale. QualityMetric, /// Tempo integration residual: maximum permitted error in /// musical_to_wallclock or wallclock_to_musical conversion. TempoIntegration, /// Solver residual: maximum permitted constraint violation /// for a soft constraint to be considered satisfied. SolverResidual, } pub struct Tolerance { pub class: ToleranceClass, /// Absolute tolerance. The unit is implied by the class. pub absolute: f64, /// Optional relative tolerance, applied to nonzero values. pub relative: Option, /// What the tolerance governs. pub governance: ToleranceGovernance, } pub enum ToleranceGovernance { /// Tolerance affects canonical equality (rare; usually only /// for diagnostic comparison). Equality, /// Tolerance is a validation threshold (constraint considered /// satisfied if violation is below tolerance). Validation, /// Tolerance affects only diagnostic output; canonical state /// is unaffected. Diagnostic, } \end{lstlisting} \begin{requirement} Specifications and conformance documents \MUSTNOT{} introduce ad-hoc epsilon constants. Every numerical tolerance that affects normative behavior \MUST{} be declared as a named \texttt{Tolerance} value belonging to a defined \texttt{ToleranceClass}, with an explicit unit, absolute and optional relative bounds, and governance category. Tolerances \MUSTNOT{} apply to identity: \texttt{PitchId}, \texttt{EventId}, \texttt{OperationId}, graph membership, hash identity, and operation ordering are exact, never within tolerance. The Quality Metric Catalog, the Reference Suite, and the Performance Reference Suite enumerate the specific tolerance values for each profile and tier. Their values are normative per the companion specifications. \end{requirement} \section{Ordered Iteration over Sets and Maps} \label{sec:det:ordering} Canonical output frequently depends on iterating a collection. Hash-map and B-tree implementation order leaks platform-specific behavior into canonical output if not controlled. \begin{requirement} Whenever canonical output, canonical serialization, or canonical hashing depends on iterating a set, map, or other collection, iteration \MUST{} occur in a specified total order. The following total orders are normative: \begin{itemize} \item Typed identifiers (\texttt{EventId}, \texttt{PitchId}, \texttt{VoiceId}, etc.): lexicographic ascending on the identifier's canonical byte form (16 bytes: 8-byte replica, 8-byte counter, big-endian). \item Operation envelopes: causal order first (from DVV closure), then HLC physical time, then HLC logical counter, then \texttt{OperationId} as final tie-break. \item Chunk references: ascending by \texttt{ChunkKind} discriminant, then by content hash lexicographic, then by file offset. \item Conflict records: ascending by \texttt{ConflictId}. \item Extension declarations: ascending by \texttt{ExtensionId}, then by semantic version lexicographic. \item Strings (text fields): byte-lexicographic on the UTF-8 NFC representation (Section~\ref{sec:det:text}). \item Rationals (\texttt{RationalTime} sequences): by numeric value ascending. \item Composite keys: lexicographic on the canonical representation of the key tuple's component fields, in declaration order. \end{itemize} Hash-map iteration order, B-tree implementation differences, and similar implementation artifacts \MUSTNOT{} leak into canonical output. \end{requirement} \section{Text and Unicode} \label{sec:det:text} Locale-dependent text behavior is a routine source of canonical divergence. The specification fixes this at the encoding layer. \begin{requirement} Canonical text fields \MUST{} be encoded as UTF-8 with Unicode NFC (Normalization Form Canonical Composition) applied. Two texts whose NFC byte representations are equal are canonically equal regardless of decomposition state. Comparisons of canonical text fields for identity \MUST{} be byte comparisons of NFC-encoded UTF-8. User-facing display collation (sorting by locale rules, case-insensitive matching, diacritic folding) is non-canonical and \MUSTNOT{} affect canonical state. Locale settings \MUSTNOT{} affect: \begin{itemize} \item Parsing of canonical text projections. \item Serialization of canonical text fields. \item Sorting of canonical map keys. \item Decimal formatting in canonical text projections (the text projection uses a fixed decimal-point convention; locale comma-vs-period preferences are irrelevant). \item Date and time formatting in canonical data (canonical timestamps use a fixed format; locale formats are for UI only). \end{itemize} Two implementations operating under different locale settings on the same canonical document \MUST{} produce identical canonical output, including identical chunk hashes and identical text projections. \end{requirement} \section{Randomness and Parallelism} \label{sec:det:rng} \subsection{Randomness} \begin{requirement} Randomness \MUSTNOT{} affect canonical output unless the random seed is an explicit canonical input. Algorithms whose results enter canonical state and that use pseudorandom generation (e.g., probabilistic constraint solvers, randomized rounding) \MUST{} consume their seed as a declared algorithm parameter that becomes part of the algorithm's conformance configuration. Identical seed plus identical input yields identical output. Identifier minting (\texttt{ReplicaId} initialization) is the only conforming use of platform-level randomness in this specification, and even there the random output enters canonical state only via the resulting identifiers. \end{requirement} \subsection{Parallelism} \begin{requirement} Parallel implementations of canonical algorithms \MUST{} produce results identical to a strict single-threaded baseline. Race timing, work-stealing order, and thread count \MUSTNOT{} affect canonical state. Where parallel reductions are used (summation over many contributions, parallel constraint evaluation), the merge step \MUST{} be deterministic: ordered reduction over a canonical ordering of contributions, not arbitrary tree-merge order. Implementations \MAY{} use parallel execution as a performance optimization. They \MAY NOT{} use it as a license for nondeterminism. \end{requirement} \section{Compression and File Bytes} \label{sec:det:compression} \begin{requirement} Canonical content identity is the uncompressed payload bytes with the domain-separated preimage (Chapter~\ref{ch:format}). Compression algorithm choice and compression level \MUSTNOT{} affect canonical identity. Identical uncompressed content has identical \texttt{ContentHash} regardless of how it was physically compressed. Canonical chunk payload encoding \MUST{} be deterministic: the same in-memory canonical structure \MUST{} produce identical uncompressed bytes across runs and across implementations, within the same schema version. The Binary Format companion specification defines the canonical encoding. Compression bytes (the physical bytes stored on disk after zstd or another algorithm is applied) are \emph{not} canonical unless a profile explicitly requires bit-identical bundle reproduction. Implementations \MAY{} recompress chunks (e.g., during compaction or repack) without altering canonical state. Readers \MUST{} verify decompressed payload against the declared content hash before treating the payload as authoritative. \end{requirement} \section{Open Algorithm Hooks} \label{sec:det:open} Two algorithms remain open questions in the body of the specification: tempo integration for arbitrary curves and root-finding for \texttt{wallclock\_to\_musical} (Chapter~\ref{ch:time}). These algorithms affect canonical state. The spelling pre-pass (Chapter~\ref{ch:pitch}) and the notational decomposition algorithm (Chapter~\ref{ch:time}) were resolved in Pass~12 to the profile-declared disposition below, each with a ratified default identifier (Requirements~\ref{req:pitch:spelling-algorithm} and~\ref{req:time:decomposition-algorithm}). \begin{requirement} Any algorithm that affects canonical state \MUST{} satisfy one of: \begin{itemize} \item It is specified normatively in this document or a named companion specification. Conforming implementations \MUST{} implement the specified algorithm. \item It is profile-declared: the active conformance profile names a specific algorithm by versioned identifier. Two conforming implementations on the same profile use the same algorithm. Profiles may differ in their algorithm choices. \item It is explicitly marked non-canonical or advisory: its output appears in the score only in non-canonical layers (caches, diagnostics, user-facing rendering hints) and \MUSTNOT{} appear in canonical chunks. \end{itemize} Implementations \MUSTNOT{} introduce algorithms affecting canonical state outside of these three categories. ``Vendor's spelling preference'' or ``proprietary decomposition heuristic'' \MUSTNOT{} silently determine canonical content. \end{requirement} \section{Conformance Statement} \label{sec:det:conformance} \begin{requirement} Conforming implementations \MUST{} declare, as part of their published conformance statement: \begin{itemize} \item Their platform and floating-point library, with the libm version or vendored math library reference. \item Their parallel execution strategy and the demonstrated equivalence to single-threaded baseline. \item Their handling of the \texttt{-0.0} canonicalization and NaN/infinity rejection. \item Their adherence to round-to-nearest-ties-to-even. \item Their compliance with the canonical iteration orders in Section~\ref{sec:det:ordering}. \item Their NFC normalization implementation reference. \item Their declared algorithms for any open-question area (spelling, decomposition, tempo curves), with versioned identifiers. \end{itemize} \end{requirement} % =========================================================================== \chapter{Canonical Byte-Layout Reference} \label{app:bytes} \providecommand{\bul}{\_\discretionary{}{}{}}% breakable underscore for long identifiers \providecommand{\cwb}{\discretionary{}{}{}}% zero-width break for long camelCase identifiers \section{Status of this reference} This appendix consolidates, in one place, every byte layout pinned by the Pass 11 ratification: the discriminant tables, derivation preimages, and primitive encodings that the core, operations, and bundle layers already implement and lock with golden-bytes tests. Its purpose is to give the Binary Format companion (Section~\ref{sec:format:binary}) a single point to import, rather than re-deriving these layouts from three separate crates. This reference is a \emph{consolidation, not a second source of truth.} Every entry cites the requirement that defines it normatively; where this appendix and a cited requirement appear to disagree, the cited requirement governs. The appendix introduces no obligation beyond what its citations already impose. The layouts that Pass 11 deliberately left to the interchange companions are listed in Section~\ref{sec:bytes:deferred} so the boundary is explicit. \section{Shared encoding conventions} \label{sec:bytes:conventions} All composite canonical encodings follow the convention baseline that the Binary Format companion inherits (Requirement~\ref{req:format:codec-conventions}): little-endian integers; a boolean as a single \texttt{0}/\texttt{1} byte; \texttt{u32} little-endian counts and a length prefix on every variable-width leaf; a tagged union as a single discriminant byte followed by its variant payload; and free-text strings as length-prefixed UTF-8 that the codec does \emph{not} NFC-fold (catalog identifiers are NFC-normalized at construction, not in the codec). Certain hashing preimages deliberately depart from the single-discriminant-byte convention where a wider or order-bearing tag is required --- notably the \texttt{TypedObjectId} 16-bit big-endian discriminant; such departures are pinned at their definitions and noted in the tables below. \section{System-derived identifier function} \label{sec:bytes:system-derived} Several identifiers below are content-addressed rather than counter-allocated. They share one function (Section~\ref{sec:graph:system-derived}): the 64-bit counter of a \texttt{ReplicaId::SYSTEM\_DERIVED} (\texttt{0xffff\_ffff\_ffff\_ffff}) identifier is $\mathrm{BE\_u64}(\mathrm{BLAKE3}(\texttt{domain\_tag} \Vert \texttt{canonical\_inputs})[0..8])$. The domain tag is exactly eight bytes. The reserved built-in tags \emph{for this derivation} are the closed set \{\texttt{MUSCSVCE}, \texttt{MUSCSPCH}, \texttt{MUSCSANM}\} --- the three system-derived-identifier tags only; the other reserved domain tags catalogued in Section~\ref{sec:bytes:tags} feed plain hashing preimages, not this counter. Tags introduced by registered extensions \MUST{} begin with \texttt{MUSCS}, be exactly eight bytes, and not collide with the reserved three. \section{Discriminant tables, derivations, and encodings} \label{sec:bytes:tables} \subsection{Object-graph identity (canonical document state)} \begin{longtable}{>{\raggedright\arraybackslash}p{3.5cm} >{\raggedright\arraybackslash}p{6.8cm} p{2.2cm}} \toprule \textbf{Layout} & \textbf{Canonical bytes / preimage} & \textbf{Requirement} \\ \midrule \endhead \texttt{TypedObjectId} tag & 16-bit big-endian discriminant (\texttt{0}--\texttt{27}) $\Vert$ variant payload. \texttt{Registered} $= 27$ encodes \texttt{reg}~(16 BE) $\Vert$ \texttt{raw}~(16 BE), 34 bytes total. \texttt{ObjectKindRegistryId} is 128-bit & \ref{req:graph:typed-object-id-discriminants} \\[2pt] Promoted \texttt{VoiceId} & System-derived under \texttt{MUSCSVCE} over a 64-byte preimage: \texttt{staff\_instance} $\Vert$ \texttt{original\_voice} $\Vert$ \texttt{winning\_op} $\Vert$ \texttt{losing\_op}, each 16-byte big-endian & \S\ref{sec:graph:promoted-voices} \\[2pt] System-derived \texttt{PitchId} & System-derived under \texttt{MUSCSPCH} over the intrinsic identity (scale position, acoustic realization); variable-width strings length-prefixed and NFC-normalized at the boundary; the tuning reference is always present (\texttt{Inherit} as a distinct presence marker) & \ref{req:graph:system-derived-pitch-id} \\[2pt] \texttt{Integrity\cwb AnomalyId} & System-derived under \texttt{MUSCSANM} over \texttt{Integrity\cwb AnomalyKind::\cwb to\bul canonical\bul bytes()} & \ref{req:graph:integrity-anomaly-id} \\[2pt] \texttt{ObjectKind} discriminant & Single byte: \texttt{Voice} $= 0$, \texttt{Pitch} $= 1$, \texttt{Registered} $= 2$. Enters the \texttt{IntegrityAnomalyId} preimage & \ref{req:graph:object-kind-vocab} \\ \bottomrule \end{longtable} \subsection{Bundle layer (chunk store, manifest, blobs)} \begin{longtable}{>{\raggedright\arraybackslash}p{3.5cm} >{\raggedright\arraybackslash}p{6.8cm} p{2.2cm}} \toprule \textbf{Layout} & \textbf{Canonical bytes / preimage} & \textbf{Requirement} \\ \midrule \endhead Chunk content hash & BLAKE3 of \texttt{domain\bul tag} $\Vert$ \texttt{ChunkKind} byte $\Vert$ \texttt{SchemaVersion} (4) $\Vert$ \texttt{uncompressed\bul length} (u64 LE) $\Vert$ \texttt{payload}; tag \texttt{MUSCCHNK} for chunks, \texttt{MUSCMANI} for the manifest. Compression is \emph{not} in the preimage & \S\ref{sec:format:hashing} \\[2pt] \texttt{ChunkKind} & Single declaration-order byte, \texttt{0}--\texttt{8} (in the chunk hash preimage, so stable) & \ref{req:format:chunkkind-discriminants} \\[2pt] \texttt{Compression\cwb Algorithm} & Fixed two bytes: discriminant $\Vert$ parameter. \texttt{None} $=$ \texttt{[0,0]}, \texttt{Zstd\{level\}} $=$ \texttt{[1,level]}, \texttt{Reserved(v)} $=$ \texttt{[2,v]}. Not part of chunk identity & \ref{req:format:chunkkind-discriminants} \\[2pt] \texttt{SchemaVersion} & \texttt{major}~(u16 LE) $\Vert$ \texttt{minor}~(u16 LE), four bytes & \S\ref{sec:format:hashing} \\[2pt] \texttt{BlobId} & Bare BLAKE3 of \texttt{MUSCBLOB} $\Vert$ \texttt{uncompressed\bul payload}: the domain tag immediately followed by payload, with no kind, schema, or length fields & \ref{req:format:blob-hash-shape} \\[2pt] \texttt{ManifestId} & \texttt{trunc128} (leading 16 bytes) of BLAKE3 of \texttt{MUSCMNIF} $\Vert$ \texttt{document\bul id} (16) $\Vert$ \texttt{generation} (u64 LE) $\Vert$ \texttt{manifest\bul body}; the \texttt{manifest\bul id} field is excluded from the body & \ref{req:format:manifest-id} \\[2pt] \texttt{ProfileId} & Fixed 20 bytes: discriminant (u32 LE; \texttt{Full} $= 0$, \texttt{ReadOnly} $= 1$, \texttt{Lite} $= 2$, \texttt{Custom} $= 3$) $\Vert$ \texttt{ProfileRegistryId}~(16, zero unless \texttt{Custom}). Load-bearing in superblock selection & \ref{req:format:profileid-discriminants} \\ \bottomrule \end{longtable} \subsection{Operation layer (transaction metadata)} \begin{longtable}{>{\raggedright\arraybackslash}p{3.5cm} >{\raggedright\arraybackslash}p{6.8cm} p{2.2cm}} \toprule \textbf{Layout} & \textbf{Canonical bytes} & \textbf{Requirement} \\ \midrule \endhead \texttt{Transaction\cwb Category} & Single byte: \texttt{NoteEntry} $= 0$, \texttt{Structural} $= 1$, \texttt{Layout} $= 2$, \texttt{Import} $= 3$, \texttt{Registered} $= 4$ & \ref{req:semops:transaction-category} \\[2pt] \texttt{ResolutionAction} & Single byte: \texttt{AcceptLoser} $= 0$, \texttt{KeepWinner} $= 1$, \texttt{Override} $= 2$, \texttt{Reanchor} $= 3$, \texttt{Dismiss} $= 4$, \texttt{Registered} $= 5$. Encoded into the operation content hash & \ref{req:semops:resolution-action-discriminants} \\ \bottomrule \end{longtable} \subsection{Primitive scalar encodings} \begin{longtable}{>{\raggedright\arraybackslash}p{3.5cm} >{\raggedright\arraybackslash}p{6.8cm} p{2.2cm}} \toprule \textbf{Layout} & \textbf{Canonical bytes} & \textbf{Requirement} \\ \midrule \endhead \texttt{RationalTime} & Sign byte (\texttt{0} zero, \texttt{1} positive, \texttt{2} negative) $\Vert$ numerator magnitude (u32-LE length-prefixed, big-endian) $\Vert$ denominator magnitude (u32-LE length-prefixed, big-endian). Always stored reduced (lowest terms, positive denominator), so equal rationals encode to equal bytes & \ref{req:format:rationaltime-encoding} \\[2pt] Wall-clock integers & Fixed-width little-endian (matching \texttt{QuantizedCoord}) & \ref{req:format:rationaltime-encoding} \\ \bottomrule \end{longtable} \section{Domain-tag registry} \label{sec:bytes:tags} Every reserved built-in eight-byte domain tag, in one place. The nine \emph{canonical} tags --- whose preimages produce identifiers and content hashes that are part of the interoperable, durably persisted form every conforming implementation must reproduce bit-identically --- are listed first; \texttt{MUSCFNTM} and \texttt{MUSCLOID} are \emph{non-canonical} (layout-only) and listed last, for completeness (see Section~\ref{sec:bytes:deferred}). Registered extensions may additionally mint \emph{system-derived} tags, which \MUST{} begin with \texttt{MUSCS}, be exactly eight bytes, and not collide with a reserved tag (Section~\ref{sec:bytes:system-derived}). \begin{longtable}{>{\raggedright\arraybackslash}p{2.6cm} >{\raggedright\arraybackslash}p{10.4cm}} \toprule \textbf{Tag} & \textbf{Use} \\ \midrule \endhead \texttt{MUSCCHNK} & Chunk content-hash preimage (\S\ref{sec:format:hashing}) \\ \texttt{MUSCMANI} & Manifest chunk content-hash preimage (\S\ref{sec:format:hashing}) \\ \texttt{MUSCBLOB} & Blob identity, bare preimage (Requirement~\ref{req:format:blob-hash-shape}) \\ \texttt{MUSCMNIF} & Manifest-id derivation (Requirement~\ref{req:format:manifest-id}) \\ \texttt{MUSCCONF} & \texttt{ConflictId} derivation (\S\ref{sec:semops:conflict-id}) \\ \texttt{MUSCENVH} & Operation-envelope hash (\texttt{EnvelopeHash}) (\S\ref{sec:semops:equivocation}) \\ \texttt{MUSCSVCE} & System-promoted voice id (\S\ref{sec:graph:promoted-voices}) \\ \texttt{MUSCSPCH} & System-derived pitch id (Requirement~\ref{req:graph:system-derived-pitch-id}) \\ \texttt{MUSCSANM} & Integrity-anomaly id (Requirement~\ref{req:graph:integrity-anomaly-id}) \\ \texttt{MUSCFNTM} & Font-metrics hash for layout conformance --- \emph{non-canonical} (\S\ref{sec:layoutir:catalog-identity}) \\ \texttt{MUSCLOID} & Layout-object id --- \emph{non-canonical}, wired by the reference implementation as of P12-I2 (Requirement~\ref{req:layoutir:object-id-derivation}) \\ \bottomrule \end{longtable} \section{Reference-implementation locks} \label{sec:bytes:goldens} Each ratified layout above is anchored by a test in the reference implementation that fails deliberately if the layout drifts. Most are \emph{golden-bytes} tests pinning the literal encoding; the two noted exceptions (\texttt{BlobId} and \texttt{RationalTime}) are anchored by round-trip and canonicalization tests, which catch a drift in decode or normalized form but not every literal-byte change. These anchors make the spec-to-code correspondence checkable. \begin{longtable}{>{\raggedright\arraybackslash}p{3.7cm} >{\raggedright\arraybackslash}p{9.3cm}} \toprule \textbf{Layout} & \textbf{Anchoring test} \\ \midrule \endhead \texttt{TypedObjectId} & {\footnotesize\texttt{epiphany-core/src/ids.rs}: \texttt{typed\bul object\bul id\bul byte\bul form\bul is\bul locked}} \\ Promoted \texttt{VoiceId} & {\footnotesize\texttt{epiphany-core/src/graph.rs}: \texttt{promoted\bul voice\bul id\bul byte\bul form\bul is\bul locked}} \\ System \texttt{PitchId} & {\footnotesize\texttt{epiphany-core/src/pitch.rs}: \texttt{system\bul pitch\bul id\bul byte\bul form\bul is\bul locked}} \\ \texttt{Integrity\cwb AnomalyId} & {\footnotesize\texttt{epiphany-ops/src/anomaly.rs}: \texttt{integrity\bul anomaly\bul id\bul byte\bul form\bul is\bul locked}} \\ \texttt{ObjectKind} & {\footnotesize\texttt{epiphany-ops/src/support.rs}: \texttt{object\bul kind\bul discriminants\bul are\bul golden}} \\ \texttt{ChunkKind} & {\footnotesize\texttt{epiphany-bundle/src/chunk.rs}: \texttt{chunk\bul kind\bul discriminants\bul are\bul golden}} \\ \texttt{Compression\cwb Algorithm} & {\footnotesize\texttt{epiphany-bundle/src/chunk.rs}: \texttt{compression\bul algorithm\bul encoding\bul is\bul golden}} \\ \texttt{ProfileId} & {\footnotesize\texttt{epiphany-bundle/src/superblock.rs}: \texttt{profile\bul id\bul discriminants\bul are\bul golden}} \\ \texttt{ManifestId} & {\footnotesize\texttt{epiphany-bundle/src/ids.rs}: \texttt{manifest\bul id\bul is\bul content\bul derived\bul and\bul deterministic}} \\ \texttt{Transaction\cwb Category} & {\footnotesize\texttt{epiphany-ops/src/payload.rs}: \texttt{transaction\bul category\bul discriminants\bul are\bul golden}} \\ \texttt{ResolutionAction} & {\footnotesize\texttt{epiphany-ops/src/conflict.rs}: \texttt{resolution\bul action\bul discriminants\bul are\bul golden}} \\ \texttt{BlobId} & {\footnotesize\texttt{epiphany-determinism/src/hash.rs}: \texttt{ContentHash::of\bul blob} and its round-trip locks \emph{(round-trip, not a literal-byte golden)}} \\ \texttt{RationalTime} & {\footnotesize\texttt{epiphany-core/src/time.rs}: \texttt{equal\bul rationals\bul encode\bul identically} \emph{(canonical-equality lock, not a literal-byte golden)}} \\ \bottomrule \end{longtable} The \texttt{MUSCLOID} layout-object-id derivation (Requirement~\ref{req:layoutir:object-id-derivation}), wired in P12-I2, is anchored differently because it is \emph{non-canonical}: its ids never enter document state or a content hash, so there is no durable byte layout to golden-lock. It is instead pinned by a derivation reference-lock test (\texttt{epiphany-layout-ir/src/provenance.rs}: \texttt{stable\bul id\bul uses\bul the\bul ratified\bul muscloid\bul derivation}), which fixes the tagged preimage; by the tag-spelling lock (\texttt{epiphany-determinism/src/domain.rs}: \texttt{exact\bul tag\bul spellings\bul match\bul spec}); and by the renderer's provenance goldens, whose \texttt{data-prov} hex changes if the derivation drifts. \section{Layouts deferred to the companions} \label{sec:bytes:deferred} The following are intentionally \emph{not} ratified here; they remain provisional pending the Binary Format companion and the Operation Catalog, and are recorded so the companion authors know precisely which layouts they own versus inherit: \begin{itemize} \item \texttt{OperationKindTag} / \texttt{OperationKind} discriminants and the full per-operation payload encodings. The reference implementation assigns provisional declaration-order discriminants (\texttt{0}--\texttt{16}) locked only for mutual distinctness, not to literal values; pinning the literal wire form is the Operation Catalog's and Binary Format companion's job. \item The full composite struct layouts --- whole-\texttt{Score}, operation envelopes, the manifest body's field order --- together with schema-version wire evolution and varint details beyond the convention baseline (Requirement~\ref{req:format:codec-conventions}). The companion inherits the baseline and formalizes these. \end{itemize} \chapter{Revision History} \label{app:history} \begin{longtable}{p{2cm} p{2.5cm} p{9cm}} \toprule \textbf{Date} & \textbf{Section} & \textbf{Change} \\ \midrule \endhead \today & All & Initial scaffold; chapter structure and front matter established. \\ \today & Ch.~\ref{ch:pitch} & Pitch chapter written: layered pitch type, scale position, acoustic realization, spelling subsystem with attachments, precedence, pre-pass, and analytical layers. Chapter structure expanded: pitch separated from time/tuning; semantic operations chapter added. \\ \today & Ch.~\ref{ch:time} & Time and duration chapter written: exact rational time with inline-or-promoted representation, distinct position and duration newtypes, wall-clock time as fixed-point nanoseconds, time anchors, time signatures with explicit beat groups including irrational meters, tempo maps with deterministic conversion, notational decomposition pre-pass, tuplets as grouping objects, and three region time models (metric, proportional, aleatoric). \\ \today & Ch.~\ref{ch:tuning} & Tuning systems and pitch spaces chapter written: pitch spaces as analytical universes distinct from tuning systems, position structures (chromatic, diatonic-over-chromatic, JI lattice, registered), interval algebras, nominal and accidental registries, SMuFL glyph references with versioning, tuning resolution variants including adaptive tuning, score-level reference pitch, hierarchical resolution rules, and the normative built-in catalog of pitch spaces and tuning systems. \\ \today & Ch.~\ref{ch:graph} & Score graph chapter written: hybrid tree-plus-cross-cutting topology, canvas-region-staff-voice-event hierarchy with regions carrying time and content models, typed 128-bit identifiers with replica-plus-counter generation, flat event arena, complete event taxonomy (pitched, unpitched, rest, indeterminate, trajectory, graphic, cue), cross-cutting registry partitioned by type, graphic content with region-local coordinates and time bindings, parts as projections separate from the canvas, analysis layers and views, graph invariants, and required indexes. \\ \today & Ch.~\ref{ch:semops} & Semantic operations chapter written: operation framework with dual command/delta layering, primitive and compound operation distinction, transactions as atomic user units, precondition classification (invariant vs. advisory) with strict authoring and lenient replay modes, the normative re-anchoring rule table, inverse-based undo with single-replica scope, semantic merge semantics with five merge families, and representative operations illustrating each major category. The full operation catalog is deferred to a separate conformance specification. \\ \today & Ch.~\ref{ch:layout-ir} & Layout IR chapter written: pipeline of four stages (LogicalLayoutIR, ConstrainedLayoutIR, ResolvedLayoutIR, RenderIR-at-interface), shared provenance discipline, spatial primitives in staff spaces with f32 precision, polymorphic time axis trait with metric/proportional/aleatoric implementations, composite logical objects flattened to glyph-level constrained objects, spring-based horizontal and vertical layout, explicit engraving decision recording, hard and soft override mechanism with preservation rules, fine-grained dependency tracking with invalidation rules, glyph catalog interface, and uniform region containers across kinds. \\ \today & Ch.~\ref{ch:format} & File format chapter written: bundle container structure with header, manifest, chunk store, and blob store; custom schema-defined binary format with self-describing extension envelope; content-addressed immutable chunks with manifest as the only mutable index; multi-level chunking (score-level, staff-region, locality-grouped cross-cutting, attachment chunks); operation log as canonical source of truth with dotted version vectors and hybrid logical clocks; snapshots as derivative caches; schema versioning with lazy migration and forward compatibility; format profiles (Full, ReadOnly, Lite); per-chunk zstd compression; atomic write protocol; deterministic s-expression text projection; streaming read guarantees. DRM and encryption explicitly excluded. \\ \today & Ch.~\ref{ch:solver} & Constraint-solver interface chapter written: interface-and-contract specification rather than algorithm; the solver trait with from-scratch and incremental modes; Cassowary- style Required and Preferred constraint strengths; the constraint family catalog (spring, collision, alignment, containment, break, aesthetic, extension); the normative quality metric set (optical spacing, collisions, page fill, casting-off, beam angle, slur curvature, vertical balance, symbol density uniformity); Pareto- bounded quality target with configurable slack and tie-breaking weights for ambiguous cases; within-implementation byte-determinism with cross-implementation Pareto equivalence; incremental solving contract with propagation bounds; structured error reporting with partial solutions on budget exhaustion; reference suite for conformance; non-normative recommendation of Cassowary as a starting algorithm. \\ \today & Chs.~\ref{ch:perf},~\ref{ch:extension} & Performance Requirements chapter expanded from skeleton: reference hardware profile (2026 desktop and tablet baselines), frame budgets for interactive edits at p99, cold-open targets, memory budgets separating core data from evictable caches, audio thread discipline requirements crossing the core/audio boundary, constraint-solver performance targets, file format performance targets, measurement methodology, regression testing recommendations. Extension Points chapter written from skeleton: consolidated catalog of every extension point in the spec organized by chapter of origin (notation grammar, tuning system, glyph, score graph, layout IR, constraint solver, file format extensions), the extension registry contract requiring versioning and identifier stability, plugin runtime considerations, conformance interactions. \\ \today & Appendices & Glossary expanded with 50+ defined terms cross-referenced to their originating chapters. Bibliography populated with engraving treatises, music theory references, tuning and microtonality sources, constraint-based layout literature, CRDT references, and prior-art notation format citations. \\ \today & All & Platform named \textit{Epiphany}. Typography refreshed to TeX Gyre Pagella (body), Heros (sans), and Cursor (mono). Color palette refined to deep teal, antique gold, warm parchment neutrals, and deep crimson accents. Title page redesigned with ornamental rules and an oldstyle classical feel. Status section, introduction, and PDF metadata updated to reflect the platform's name. \\ \today & Pass 1 revision (Chs.~\ref{ch:pitch},~\ref{ch:time},~\ref{ch:tuning},~\ref{ch:graph},~\ref{ch:semops}) & Type-seam corrections in response to external review. Introduced \texttt{IdentifiedPitch} wrapping every embedded pitch with a stable \texttt{PitchId}; updated \texttt{PitchedEvent}, \texttt{TrajectoryEndpoint}, and \texttt{TrajectoryShape::Stepwise} accordingly. Introduced \texttt{EventDuration} union covering musical, wall-clock, and indeterminate cases, with concrete-only \texttt{DurationBounds} avoiding recursive indeterminacy. Aleatoric regions now declare an explicit \texttt{AleatoricAnchoringDiscipline}. \texttt{PitchSpelling} carries a stack of accidentals (\texttt{Vec}) with normative additive semantics and duplicate-detection invariants, replacing the prior \texttt{Option}. \texttt{PitchSpacePosition} gains a \texttt{JiVector} variant for first-class just-intonation lattice positions. The default CMN pitch space is now correctly described as \texttt{cmn-12} (diatonic-over-chromatic), not pure chromatic. Chapter~\ref{ch:pitch}'s reference-pitch text rewritten to defer to Chapter~\ref{ch:tuning}, resolving the prior ownership contradiction. \texttt{MusicalPosition} and \texttt{MusicalDuration} now derive \texttt{Clone}, not \texttt{Copy}, with a permitted inline-or-pointer-tagged implementation noted as conforming. \texttt{Tie::pitch\_pairing} now uses \texttt{Vec<(PitchId, PitchId)>} instead of positional indices. The duplicate \texttt{AnalysisLayer} definition in Chapter~\ref{ch:pitch} was removed in favor of the authoritative definition in Chapter~\ref{ch:graph}. Graph invariants expanded to cover identified pitches, tombstoning, time-model agreement, and \texttt{PitchId}-pairing tie targets. Representative operations (\texttt{InsertEvent}, \texttt{DeleteEvent}, \texttt{RespellPitch}, \texttt{Transpose}) updated for atomic ID minting, pitch tombstoning, voice-promotion on concurrent insertion, and \texttt{PitchId} preservation through transposition. Status downgraded to ``structural working draft.'' \\ \today & Pass 2 revision (Chs.~\ref{ch:time},~\ref{ch:graph},~\ref{ch:semops}) & Graph and time invariants strengthened in response to external review. \texttt{TimeAnchor} offsets generalized via the \texttt{AnchorOffset} union (Musical, WallClock, Zero), with normative agreement between offset variant and target region's time model. \texttt{TempoSegment} restructured to carry explicit \texttt{end: Option}, \texttt{start\_tempo}, \texttt{end\_tempo: Option}, and \texttt{shape}; tempo segments are required to be monotonic in musical time so that wall-clock-to-musical conversion is single-valued. Polymeter admitted natively: \texttt{StaffBasedContent} now carries \texttt{Vec} (region-local), plus \texttt{default\_metric\_grid: Option} and \texttt{barline\_alignment\_groups: Vec}. Measures and metric grids are per-staff-instance, not per-region. Staff identity split into the global \texttt{Staff} (declared once at score level) and the region-local \texttt{StaffInstance}; \texttt{Score} top-level structure expanded with \texttt{staves: Vec} and \texttt{staff\_groups: Vec}. \texttt{DeleteEvent} preconditions strengthened: target events belonging to tuplets require explicit \texttt{TupletCompensation} (\texttt{NotInTuplet}, \texttt{ReplaceWithRest}, \texttt{RewriteTuplets}, \texttt{CascadeDeleteTuplets}). Tie validation reframed from universal immediate-adjacency to class-specific profiles: \texttt{TieClass} (Standard, Editorial, CrossVoice, LaissezVibrer, Registered) admits editorial, cross-voice, and laissez-vibrer ties without relaxing standard-tie invariants. System-promoted voices formalized: \texttt{VoiceOrigin} enum (UserDeclared, Imported, SystemPromoted); promoted voices have deterministically-derived \texttt{VoiceId}s, are first-class graph objects, are visible to users, and are normalized only by explicit user operation. Graph invariants expanded from 14 to 18 normative invariants reflecting staff-instance ontology, per-staff measures, anchor-offset agreement, system-promoted voice identity, and barline-alignment-group containment. New identifier types: \texttt{StaffInstanceId}, \texttt{StaffGroupId}, \texttt{BarlineAlignmentGroupId}, \texttt{TimeSignatureId}. \\ \today & Pass 3 revision (Ch.~\ref{ch:semops},~\ref{ch:format}) & Concurrent semantics rewritten from pairwise operation merge to operation-set deterministic reduction. The replicated operation set is now framed as a true CRDT (a grow-only set of envelopes); the materialized score graph is its deterministic reduction, not itself a CRDT. \texttt{OperationId} (replica + counter) separated from \texttt{OperationStamp} (HLC + id); canonical reduction order is causal-then-HLC-then-lexicographic. Causal contexts expressed as dotted version vectors (\texttt{CausalContext}), not exhaustive predecessor lists; exhaustive lists are explicitly forbidden as canonical representation. \texttt{OperationEnvelope} introduced with id, author, stamp, causal context, optional transaction, payload. Four-phase operation lifecycle specified: Prepare, Commit, Reduce, Report. Operation effects formalized: \texttt{Applied}, \texttt{AppliedWithRepair}, \texttt{Conflicted}, \texttt{TombstonedTarget}, \texttt{NoOp\{reason\}}; every replica reducing the same set records the same effects with the same reasons. Conflict records made first-class graph objects in a score-level \texttt{ConflictRegistry}: stable, addressable, user-visible, resolvable by subsequent \texttt{ResolveConflict} operations. Tombstones formalized with explicit retention rules (pending replication, undo, conflict resolution, attachment resolution). Re-anchoring rewritten as a total deterministic function: ``nearest'' is now a strict lexicographic minimum over (containment proximity, time distance, direction preference, object id), with no implementation discretion. Re-anchoring rule table updated for the new staff-instance ontology and the deterministic ``nearest'' function. Transactions formalized as atomic replicated groups: primitive members \MUSTNOT{} materialize as independently visible intermediate states; failures conflict the whole transaction. Undo redefined as a forward compensating operation (\texttt{UndoTransaction}) with three policies (\texttt{StrictInverse}, \texttt{BestEffort}, \texttt{Cascade}); bit-identical undo demoted to content equivalence. LWW reduction restricted to explicitly-marked \texttt{LwwAdvisory} scalar fields (metadata, view preferences, layout hints, system/page break preferences, optional cross-cutting display hints); structural musical fields \MUSTNOT{} default to LWW. Cache invalidation rule: caches are acceleration only, never canonical; invalidated on operation-set, causal-order, reduction-algorithm, or profile change. Per-operation merge families renamed to reduction rules and rewritten in the new model; \texttt{ChangeRegionTimeModel} now correctly conflicts rather than silently coercing incompatible contained events; \texttt{SetUserSystemBreak} explicitly marked LwwAdvisory; \texttt{RespellPitch} concurrent collisions preserve the losing spelling in a structural-field-collision conflict record. New identifier types: \texttt{ConflictId}, \texttt{TransactionId}. Operation log restructured to store envelopes rather than primitive operations with separate stored-sequence metadata. \\ \today & Pass 4 revision (Ch.~\ref{ch:format}) & File-format restructured around Pass 3's operation-set canonical-document model. The earlier ``single mutable manifest'' story replaced with a fixed 64-byte header plus two fixed-size 256-byte superblock slots; the superblocks are the only mutable on-disk objects, and atomic commit is achieved by writing the inactive superblock slot with generation $+ 1$. Active superblock selected by highest valid generation. Generation gap greater than one detected as integrity anomaly with read-only recovery mode. Manifest reframed as a table of roots and declarations (operation\_roots, retained\_snapshots, blob\_roots, profile\_declarations, extension\_declarations, text\_projection root, integrity\_root); user-facing metadata explicitly removed from the manifest in favor of operation envelopes and reduced state. BLAKE3 fixed as the single content-hashing algorithm, replacing the prior ``BLAKE3 or SHA-256'' uncertainty. Hash preimage domain-separated and explicitly excludes \texttt{compression\_algorithm} to preserve deduplication across compression choices. Operation-envelope blocks specified as the canonical storage unit with a soft 1\,MiB uncompressed target size and a default 64\,MiB upper bound per profile. Snapshots separated into acceleration snapshots (caches, discardable) and canonical-base snapshots (declared in \texttt{retained\_snapshots}, required for pruning). Pruning protocol specified: canonical state preserved as (base snapshot $+$ post-frontier envelopes). Atomic write protocol specified step-by-step with explicit durable-flush points; the durable flush is platform-abstracted to cover \texttt{fsync}, \texttt{FlushFileBuffers}, and equivalents. Crash recovery rules enumerated by failure point. Garbage collection specified as conservative, optional, deferred; never on the commit critical path. Edit barriers formalized as a structured first-class concept (\texttt{EditBarrier} with scope, affected object kinds, prohibited operation kinds, and condition), with an unsafe-edit escape hatch that tombstones extension data rather than silently corrupting it. Text projection reframed: semantic round-trip required (envelopes, declarations, reduced state); physical-layout fidelity (chunk offsets, compression choices, cache chunks, garbage regions, superblock generation) explicitly not required. Schema versioning, format profiles (Full, ReadOnly, Lite, Custom), and compression sections updated for the new architecture. Streaming-read cold-open procedure rewritten to use snapshot $+$ envelope-stream model. \\ \today & Pass 5 revision (Ch.~\ref{ch:solver}) & Solver conformance reframed from universal Pareto optimality to reference-suite-based conformance with profile-specific thresholds. The earlier ``no better Pareto layout exists'' obligation, which quantified over an undecidable space of possible layouts, has been replaced with checkable, falsifiable thresholds on a reference suite. Solver obligations split into four classes: validity conformance (hard constraints, provenance, unit system, invariants), determinism conformance, diagnostic conformance (\texttt{SolveStatus}, \texttt{SolveReport}, warnings, unsatisfied-constraint reporting, metric vectors), and quality conformance (reference-suite thresholds). Three solver tiers introduced (\texttt{Minimal}, \texttt{Standard}, \texttt{Advanced}) with declared obligations per tier; tiers are orthogonal to file-format profiles. Deterministic budgets formalized (\texttt{max\_iterations}, \texttt{max\_nodes}, \texttt{max\_constraint\_evaluations}); wall-clock time demoted to advisory only. Wall-clock \MUSTNOT{} change the canonical layout. Quality metrics normalized: \texttt{NormalizedMetric} is a finite \texttt{f64} in $[0.0, 1.0]$ with $0.0$ best and $1.0$ worst; orientation and range fixed across all metrics including extension metrics. Hard constraints explicitly separated from quality metrics: a solver \MUSTNOT{} trade off a hard-constraint violation for a better quality score. Within-implementation determinism (byte-equal output for identical input + version) separated from cross-implementation conformance (reference-suite thresholds, not byte equality). \texttt{SolveReport} unified the prior \texttt{Result} return: every invocation yields a report with a \texttt{SolveStatus} (\texttt{Solved}, \texttt{SolvedWithWarnings}, \texttt{PartialBudgetExhausted}, \texttt{Unsatisfiable}, \texttt{InternalError}). \texttt{PartialBudgetExhausted} preserves hard-constraint validity; budget exhaustion that cannot satisfy hard constraints returns \texttt{Unsatisfiable}. Six invalidation scopes specified (\texttt{ObjectLocal}, \texttt{MeasureLocal}, \texttt{SystemLocal}, \texttt{PageLocal}, \texttt{RegionLocal}, \texttt{WholeScore}); incremental solving contract reframed as observational equivalence to scoped full solving (solvers \MAY{} widen scopes conservatively but \MAY NOT{} narrow them). Reference algorithm demoted from ``recommended starting point'' to explanatory and diagnostic only; conforming implementations \MAY{} choose any algorithm. Pass 4 rollback-language corrected: rollback is achieved by retained manifests in the chunk store, not by retaining multiple on-disk superblock generations (the fixed layout has only two slots). \\ \today & Pass 6 revision (App.~\ref{app:determinism}) & New Determinism Contract appendix consolidates reproducibility obligations scattered across Chapters~\ref{ch:pitch}--\ref{ch:solver} into a single normative reference. Five layers of determinism distinguished: canonical score determinism (byte-equal cross-implementation), canonical serialization determinism (byte-equal chunk hashes and text projection), layout determinism (byte-equal within implementation version), conformance determinism (reference-suite-based, not byte-equal cross-implementation), and non-canonical cache determinism (caches may vary, MUST NOT affect canonical state). Canonical floating-point values restricted to finite IEEE 754 binary64; NaN and infinity prohibited from canonical chunks; \texttt{-0.0} canonicalized to \texttt{+0.0} before storage and hashing. Little-endian IEEE 754 binary64 serialization fixed. IEEE 754 round-to-nearest, ties-to-even mandated for canonical numerical algorithms; \texttt{-ffast-math} and equivalents prohibited; reassociation, distribution, and implicit fused-multiply-add forbidden where they could change canonical output. Quantized layout coordinates introduced (\texttt{QuantizedCoord} at $1/1024$ staff space) so canonical \texttt{ResolvedLayoutIR} positions are stable across implementations whose internal computations agree to within half-quantum. Tolerance classes formalized (\texttt{AcousticCents}, \texttt{LayoutCoordinate}, \texttt{QualityMetric}, \texttt{TempoIntegration}, \texttt{SolverResidual}) with governance category (\texttt{Equality}, \texttt{Validation}, \texttt{Diagnostic}); ad-hoc epsilons prohibited; tolerances MUST NOT apply to identity. Canonical iteration order specified for every enumerable collection (identifiers lexicographic on canonical byte form; operations causal-then-HLC-then-id; chunk references by kind, hash, offset; conflicts by id; extensions by id then version; strings byte-lexicographic on NFC UTF-8). Text normalization fixed at UTF-8 NFC; locale prohibited from affecting canonical parsing, serialization, sorting, decimal formatting, or projection. Randomness MUST NOT enter canonical state unless the seed is an explicit canonical input. Parallelism permitted but MUST produce results identical to a strict single-threaded baseline through deterministic ordered reductions. Compression confirmed as non-canonical (identical uncompressed content has identical hash regardless of compression choice); recompression permitted without altering canonical state. Open algorithm hooks (spelling pre-pass, decomposition, tempo integration, root-finding) required to be either normatively specified, profile-declared by versioned identifier, or explicitly non-canonical/advisory; vendor freedom over canonical algorithms prohibited. Conformance statement enumerates the required disclosures implementations must publish (libm, parallelism strategy, NFC implementation, declared algorithms for open-question areas). Status section updated to reference Appendix~\ref{app:determinism}. \\ \today & Pass 7 revision (editorial, all chapters) & Editorial consistency pass: no architectural changes, only cleanup. Resolved the contradiction between Appendix~\ref{app:determinism}'s ``byte-equal canonical state cross-implementation'' and the existence of open canonical algorithms by adding an explicit conditional on the disposition of Section~\ref{sec:det:open}. Stale section-number cross- references replaced with symbolic labels (Section~2.4 $\to$ \texttt{sec:pitch:spelling}; Section~2.4.4 $\to$ \texttt{sec:pitch:precedence}; Section~5.9.3 $\to$ \texttt{sec:graph:timebinding}; Section~6.6 $\to$ \texttt{sec:semops:conflicts}; Section~6.8 $\to$ \texttt{sec:semops:undo}). Terminology normalized: ``merge family'' $\to$ ``reduction rule'' where the operation framework was meant; ``Pareto-bounded quality model'' $\to$ reference-suite/normalized-metric language in extension points; glossary entries for Operation log, Operation set, Operation envelope, Canonical reduction, Pareto frontier (design target), Staff, Staff instance, Conflict record, CRDT, Snapshot, and Transaction rewritten or added. New Appendix~\ref{app:deferred} (Intentionally Deferred Types and Specifications) catalogs companion specifications (Binary Format, Text Projection, Profile Conformance, Quality Metric Catalog, Reference Suite, Performance Reference Suite, Operation Catalog, Reference Algorithm), externally provided components (SMuFL, font metric provider, audio engine, plugin runtime, collaboration transport, user interface), open canonical algorithms, and extension registry catalogs; distinguishes deliberately external content from accidentally missing content. Status section rewritten to reflect structural stabilization: substantive review passes complete, architecture not under active redesign, remaining blockers are companion specifications and open canonical algorithms; the section enumerates what conformance can be established now and what awaits companion delivery. Revision history entries for Passes 1--7 read as the document's architectural-stabilization log, consolidating the design decisions taken in response to external review. \\ \today & Pass 8 revision (post-review correctness pass) & Determinism correctness pass addressing review of the Pass 7 output. \texttt{ConflictId} reframed as content-derived via BLAKE3 truncation with domain tag \texttt{"MUSCCONF"}, fixing a determinism leak where each replica would have minted its own conflict identifiers from local replica-counter sequences; conflict records produced during reduction now have identifiers that agree across replicas by construction. \texttt{RepairRecord} defined (was referenced by \texttt{OperationEffect::AppliedWithRepair} without definition), with a \texttt{RepairKind} enumeration covering \texttt{Reanchored}, \texttt{SpannerTruncated}, \texttt{Orphaned}, \texttt{CascadeDeleted}, \texttt{AttachmentTombstoned}, \texttt{VoicePromoted}, \texttt{TupletCompensated}, and \texttt{Registered}. \texttt{TypedObjectId} defined as a tagged union over every identifier kind in the score graph, with canonical-bytes serialization for hashing/ordering/equality. \texttt{OperationKind} defined as a tagged enumeration of primitive operation variants, plus a \texttt{Registered} variant for extension-defined primitives. System-derived identifier namespace formalized: \texttt{ReplicaId::SYSTEM\_DERIVED} reserved at \texttt{0xffff\_ffff\_ffff\_ffff}, with the 64-bit counter derived deterministically via BLAKE3 truncation; domain tags for system identifiers (\texttt{"MUSCSVCE"} for system-promoted voices, \texttt{"MUSCSPCH"} for system-derived pitches) enumerated; user-authored replicas \MUSTNOT{} use the reserved value. Envelope-acceptance rules added: a well-formed envelope requires non-system replica id, finite physical time, well-formed DVV, deserializable payload; per-replica monotonicity enforced; clock-skew warnings allowed but reordering for plausibility prohibited; authentication deferred to collaboration transport. Canonical-base snapshot semantics repaired: manifest split into single \texttt{canonical\_base: Option} plus \texttt{acceleration\_snapshots: Vec}; coverage test specified as DVV-frontier membership, explicitly forbidding HLC-stamp comparison for canonicality determination; pruning replaces the canonical base atomically. Header CRC moved to the last 4 bytes of the 64-byte fixed header, resolving a circular-definition bug where the CRC field appeared inside its own coverage range. Manifest chunks \MUST{} be stored uncompressed in this format version, resolving the bootstrap chicken-and-egg problem (a reader needs to know how to decode the manifest before it has any manifest information). \texttt{ChunkId} defined as a newtype around \texttt{ContentHash}, clarifying the prior comment that the hash and id were redundant. Three-layer identity model for documents: \texttt{file\_uuid} (physical bundle, changes on Save As), \texttt{document\_id} (logical work, persists across Save As by default), optional \texttt{lineage\_id} (shared ancestor for genealogy/version-control purposes); Save As semantics specified normatively. \texttt{RetentionPolicy} defined as a first-class concept with \texttt{retain\_previous\_manifests}, \texttt{retain\_duration}, \texttt{retain\_named\_checkpoints}; rollback achieved via retained manifests in the chunk store, not multiple on-disk superblock generations. \texttt{BarrierCondition} defined (\texttt{Always}, \texttt{ObjectExists}, \texttt{ObjectHasExtensionData}, \texttt{All}, \texttt{Any}, \texttt{Not}, \texttt{Registered}), making the edit-barrier model implementable for unknown extensions. \texttt{GlyphCatalogIdentity} defined in Chapter~\ref{ch:layout-ir}, required for any byte-equal-output layout conformance claim; without it, layout output is implementation-specific. \texttt{Box} replaced with \texttt{TimeAxisModel} enum (Metric, Proportional, Aleatoric, Registered), giving the layout IR a canonical representation for serialization, hashing, and conformance comparison while still permitting internal trait-object dispatch. Performance chapter restructured around an explicit core/product/Performance-Reference-Suite obligation split: core obligations bound the core's own work (reduction, layout, file writes, snapshot acquisition), while end-to-end UI/audio targets are product-layer obligations specified in companion documents. Editorial sweeps: remaining ``merge semantics'' prose in Chapter~\ref{ch:intro}, the compound transpose primitive, the \texttt{VoiceOrigin::SystemPromoted} comment, and the extension-point design principles updated to ``reduction semantics'' or ``reduction rules''; tempo curve, decomposition, and spelling pre-pass open questions updated to reference Appendix~\ref{app:determinism} Section~\ref{sec:det:open} dispositions. Glossary additions: Edit barrier, Glyph catalog identity, Operation kind, Repair record, Retention policy, Typed object identifier, Document identifier (with cross-references to FileUuid and LineageId). Reading Guide canonical-vs-non-canonical table updated to reflect the canonical\_base / acceleration\_snapshots split. \\ \today & Pass 9 revision (correctness and consistency pass) & Operation-envelope duplicate handling formalized into three outcomes: \texttt{IdempotentDuplicate} (same id, byte-identical envelope: silently dropped), \texttt{FirstAcceptance} (new id: accepted after well-formedness check), and \texttt{EnvelopeEquivocation} (same id, different canonical bytes: divergent envelope rejected, integrity diagnostic surfaced, bundle non-conforming if equivocation appears on-disk). Well-formedness invariant \texttt{envelope.stamp.id == envelope.id} made normative. HLC monotonicity reframed as a property of the set of accepted envelopes, not arrival order: out-of-order delivery is fine; on detection of inconsistency, both envelopes are retained but the offending replica's stream from the violating counter onward is quarantined and the document is flagged with a replica-equivocation anomaly. The free-form \texttt{NoOpReason::PreconditionFailedUnderReduction { reason: String }} replaced with a typed \texttt{PreconditionFailureReason} enum, closing a determinism leak (free-form strings in canonical state could otherwise differ across implementations). Transaction descriptor ordering integrated into canonical reduction via causal dependency: every primitive member of a transaction \MUST{} causally depend on its \texttt{DeclareTransaction} envelope; members lacking such a dependency reduce to \texttt{NoOp\{TransactionConflict\}} with a synthesized conflict record. The fixed-header \texttt{file\_uuid} requirement reworded to match the Pass 8 identity model: fixed for the lifetime of a physical bundle, preserved by filesystem byte-copy, freshly minted on Save As. Manifest \texttt{integrity\_root} made \texttt{Option} (it is an accelerator); new ``Canonical and Non-Canonical Manifest Roots'' subsection partitions the manifest's fields by their role in canonical state, with the rule that failed verification of non-canonical chunks is rebuildable rather than corruption. Cold-open procedure rewritten to reflect the canonical-base / operation-envelope-block model (the prior text retained pre-Pass-3 region-chunks language). Streaming- read locatability list updated. System-derived counter collisions: mandatory collision check during reduction; collision becomes hard corruption via new \texttt{ConflictKind::SystemIdentifierCollision} record naming both colliding canonical-input sets. \texttt{GlyphCatalogIdentity} \texttt{metrics\_hash} scope broadened: hash covers glyphs referenced by the \texttt{ConstrainedLayoutIR} (solver inputs), not just glyphs in the final \texttt{ResolvedLayoutIR}. \texttt{CommitState} validation added to superblock selection: non-\texttt{Committed} superblock is not valid for ordinary selection. Schema-version fallback refined into three cases by chunk role: canonical chunks with unsupported major schema require read-only preservation mode (or refusal); non-canonical chunks may be opaquely preserved; unknown canonical operation payloads require extension support or open read-only without materialization. New \texttt{OperationKindTag} (discriminator- only enum) introduced for edit barriers, conformance reports, and diagnostic surfaces; \texttt{EditBarrier} switched to \texttt{prohibited\_operation\_kinds: Vec}. ``Absolute musical positions \MUSTNOT{} appear in stored data'' rule narrowed: stored \emph{references to external time points} use \texttt{TimeAnchor}; events' \emph{own positions} within their owning voice and region may be stored as \texttt{EventPosition}. New Appendix~\ref{app:deferred} Section~\ref{sec:deferred:support-types} catalogs the semantic identity rules for \texttt{FileUuid}, \texttt{DocumentId}, \texttt{LineageId}, \texttt{ManifestId}, \texttt{SnapshotId}, \texttt{BlobId}, \texttt{AuthorId}, \texttt{ReductionAlgorithmVersion}, \texttt{SolverProfile}, \texttt{ProfileId}, \texttt{ContentHash}, \texttt{ChunkId}, \texttt{OperationId}, \texttt{TransactionId}, \texttt{ConflictId}, and the typed identifier family. Editorial: \texttt{MUSTNOT} and \texttt{SHOULDNOT} macros switched to \texttt{nobreakspace} so \texttt{pdftotext} extracts ``MUST NOT'' as two tokens; Manifest, Operation log, and Snapshot glossary entries updated to match the \texttt{canonical\_base} / \texttt{acceleration\_snapshots} manifest field names. \\ \today & Pass 10 revision (order-independence and integrity- anomaly cleanup) & \texttt{OperationId} equivocation reworked from a first-arrival rule (which made canonical state arrival-order-dependent across replicas observing the same envelopes in different orders) to an order-independent \texttt{OperationSlot} model. The slot is either \texttt{Single(OperationEnvelope)} or \texttt{Equivocated{} \{ operation\_id, candidates: BTreeSet \}}; observing a second distinct canonical envelope under an \texttt{OperationId} transitions the slot to \texttt{Equivocated} regardless of arrival order. Equivocated slots produce no canonical reduction effect; both candidates are retained for diagnostic recovery. Operations causally depending on an equivocated id are held pending resolution. Resolution is by explicit policy: transport-level credential revocation, local user reconciliation, or a profile-declared deterministic selection function. No arrival-order default applies. HLC monotonicity-anomaly handling sharpened: a new \texttt{AnomalousReplicaSegment} type captures the offending replica, the first violating counter, the reason, and the excluded envelope ids. Operations from the violating counter onward are retained but \emph{excluded from canonical reduction}; canonical state is derived only from envelopes that satisfy the monotonicity invariant. The earlier ``quarantined'' wording is now backed by an explicit structural exclusion rule. System-derived ID collisions moved out of \texttt{ConflictKind} and into a new \texttt{IntegrityAnomaly} type with kinds \texttt{SystemIdentifierCollision}, \texttt{OperationSlotEquivocated}, and \texttt{ReplicaStreamQuarantined}. The distinction is that conflict records are ordinary canonical-state facts (a representable musical conflict between two valid edits) while integrity anomalies indicate the structural assumptions underlying canonical state have failed and require external recovery; an integrity anomaly takes the document out of ordinary canonical operation. Stale ``refined in a later revision'' parenthetical removed from the \texttt{DeleteEvent} reduction rule; the re-anchoring total order is part of the current architecture and needs no further refinement. Typo fixed in the superblock-selection requirement (\texttt{MAY{}-be-valid} replaced with the prose ``invalid for ordinary selection but may be inspected in diagnostic recovery mode''). Glossary chapter references for ``Dotted version vector'' and ``Hybrid logical clock'' updated to ``Chapters 6, 8'' with explicit ``defined operationally in Chapter~\ref{ch:semops}; serialized in Chapter~\ref{ch:format}'' wording, reflecting that the Pass 3 architectural shift made causal context part of operation semantics rather than only file storage. \\ \today & Pass 11 revision (spec ratification) & Converted the v0 implementation's provisional, golden-locked byte choices into ratified normative spec text; the architecture is unchanged. \emph{Adopt-and-pin (bytes):} pinned the \texttt{TypedObjectId} 16-bit big-endian discriminant table (\texttt{0}--\texttt{27}, \texttt{Registered}~=~27; added the \texttt{Tuplet}/\texttt{RepeatStructure}/\texttt{LyricLine}/% \texttt{ChordSymbol}/\texttt{View} variants the implementation carried); the promoted-voice (\texttt{MUSCSVCE}), system-pitch (\texttt{MUSCSPCH}, tuning always part of identity), and integrity-anomaly (\texttt{MUSCSANM}, promoted to a reserved built-in tag) derivations; the \texttt{ChunkKind} / \texttt{ProfileId} / \texttt{CompressionAlgorithm} discriminants; the \texttt{ManifestId} preimage (with \texttt{manifest\_id} excluded); and the \texttt{RationalTime}/scalar primitive layouts plus the shared codec convention baseline the Binary Format companion inherits. \emph{Decide-and-pin:} \texttt{TempoShape::Linear} interpolates \emph{speed} (whole notes/second), not bpm; a \texttt{StructuralFieldCollision} tags the \emph{winner} \texttt{Conflicted}; lifted the $>$2-way voice-promotion generalization (partial-overlap, lowest-id-survivor) to normative; pinned the \texttt{TransactionCategory} and \texttt{ObjectKind} core vocabularies; added \texttt{ResolutionAction::Dismiss} so the \texttt{Dismissed} resolution state is reachable by an authored operation; and specified the (non-canonical) \texttt{LayoutObjectId} derivation with a \texttt{MUSCLOID} tag as the Track~A target (then deferred to Track~A; subsequently wired by the reference implementation in P12-I2). \emph{Fixes:} blob hashing is the bare \texttt{"MUSCBLOB" || payload} (deleted the contradictory ``identically to chunks'' phrasing); added the equal-generation superblock rule (\texttt{DivergentSameGeneration} on load-bearing-field divergence); defined \texttt{ProfileConstraints} and placed the required \texttt{RetentionPolicy} in it with first-declared multi-profile precedence; made the DVV zero-based counter floor normative; reconciled the graph-invariant count to 19 (was 18 in QUICKSTART) and named the three construction-time MUSTs (time-signature beat-group sum, ordering-DAG acyclicity, and non-degenerate \texttt{TupletRatio}, now enforced at construction). \emph{Consolidation:} added the Canonical Byte-Layout Reference (Appendix~\ref{app:bytes}) gathering every ratified discriminant, derivation preimage, and primitive encoding into one place for the Binary Format companion to import, with a domain-tag registry and golden-lock anchors; and closed two follow-through gaps where the discriminant \emph{bytes} of \texttt{ObjectKind} (\texttt{Voice}~=~0, \texttt{Pitch}~=~1, \texttt{Registered}~=~2; feeds the \texttt{IntegrityAnomalyId} preimage) and \texttt{ResolutionAction} (\texttt{AcceptLoser}~=~0~$\dots$~\texttt{Registered}~=~5; feeds the operation content hash) were golden-locked in code but unpinned in spec text. \\ \today & Pass 12 tranche 1 (audit spec-alignment) & Aligned spec text with dispositions the project had already made and the reference implementation exercises; no byte-layout or architecture change. The re-anchoring rule table's Slur and Spanner rows now state the Operation Catalog's surviving-endpoint semantics (re-anchor while $\geq 1$ endpoint survives, cascade-delete only when none does; proximity-aware re-targeting deferred). \texttt{TupletCompensation::RewriteTuplets} adopts the catalog's id-only v1 payload (\texttt{TupletRewrite} removed): graph-aware reduction refuses the variant rather than fabricate rewritten values, and a value-carrying payload revision reopens it. Operation-envelope blocks are pure envelope vectors (a content-addressed chunk cannot embed its own id); the per-block summary metadata (\texttt{dvv\_summary}/\texttt{min\_stamp}/\texttt{max\_stamp}) relocated to the manifest's \texttt{operation\_block\_summaries} map (opaque ops-supplied bytes, non-canonical, ChunkId-keyed), and the order-blocks-by-\texttt{min\_stamp} \SHOULD{} was dropped because the canonical manifest encoding sorts chunk references by encoded form. The spelling and decomposition pre-passes are now specified as producing \emph{canonical derived annotations} --- deterministic functions of (materialized graph, profile, versioned algorithm id), recomputed on materialization and never stored --- replacing the stored-\texttt{Inferred}-attachment model; the incremental re-run requirement is demoted from \MUST{} to \MAY{} with caching and incrementality required to be unobservable. Spelling-precedence ties now break by canonical attachment order (attachments carry no creation timestamp). The \texttt{ConflictRegistry} moved off the \texttt{Score} root into canonical materialized state, matching Chapter~6. \texttt{SolverTier} gains the non-conformance \texttt{Stub} variant. Companion: Operation Catalog 0.2.0 $\rightarrow$ 0.3.0 (ModifyEvent materializes metric placement changes behind a voice-occupancy placement precondition). Dispositions logged in \texttt{spec/PASS12\_RATIFICATION\_LOG.md}; the Pass 12 batch itself remains open. \\ \today & Pass 12 tranche 1 addendum (Push-3 enablers) & Two enabler ratifications for the machinery-wiring work. \texttt{ResolveEquivocation}, previously a dangling prose reference in the equivocation section, is now a defined meta-operation: \texttt{OperationPayload} gains the variant here, and the Operation Catalog (0.3.0 $\rightarrow$ 0.4.0) pins its payload schema (\texttt{\{ target: OperationId, chosen: EnvelopeHash \}}), the order-independent earliest-resolve-governs promotion, idempotence, differing-resolve meta-conflict, and precondition no-ops; the profile-declared selection-policy path remains open (P12-K5). Engraving break overrides became position-addressable: \texttt{OverrideKind::SystemBreak}/\texttt{PageBreak} carry the break's \texttt{TimeAnchor} (the \texttt{ScoreGraph} target names the owning region; projected break overrides carry \texttt{Internal} origin pending P11-C8 authorship surfacing), closing the representational gap that blocked the logical-stage override projection §Engraving Overrides requires. \\ \today & Phase-3 first tranche (casting-off + catalog) & Companion movements, no core-spec byte or architecture change. The Operation Catalog (0.4.0 $\rightarrow$ 0.5.0) gains the Phase-3 schema-fill tranche --- \texttt{CreateStaff} (set-union global-staff mint, plus a staff-liveness precondition on \texttt{CreateStaffInstance}), \texttt{SetTimeSignature} and \texttt{SetTempoSegment} (LWW structural overwrites beneath the whole-grid \texttt{SetMetricGrid}), \texttt{SetStaffLayout} (LWW advisory) --- and a rewritten \sectionsc{UndoTransaction}: \texttt{StrictInverse}/\texttt{BestEffort} now perform \emph{value restoration} for overwrite primitives via canonical-order write chains (delete resurrection, Transpose inversion, and Cascade closure stay deferred, P11-C8 narrowed). The Binary Format companion (0.1.0 $\rightarrow$ 0.2.0) appends the corresponding wire discriminants. This document's \texttt{OperationKind} listing gains the four kinds. The create score/canvas slots remain deliberately unavailable pending an addressable root model (P12-K8). \\ \today & Schema major 1 (data-model expansion) & Defines the two referenced-but-undefined types \texttt{CanvasLayoutDefaults} (with \texttt{CanvasSize} / \texttt{CanvasMargins}, staff-space page geometry, A4/8\,mm default; closes P12-I7) and \texttt{PitchRange} (advisory pitch compass, used by \texttt{Instrument.range} and \texttt{IndeterminacyHints}); adds \texttt{Region.permits\_spanning\_slurs} (default false, P12-K7). Records that schema major~1 is the first data-model expansion major: the canonical-base \texttt{MaterializedState} embeds none of these values and stays major~0, byte-identical, but \texttt{Region.permits\_spanning\_slurs} reaches the \emph{canonical} \texttt{CreateRegion} operation payload, so a major-0 reader opens a bundle carrying v1 \texttt{CreateRegion} ops read-only. The wire form, the resolved-layout length-prefix unification (the manifest-embedded barrier blobs stay \texttt{u64}), and the byte-for-byte v0${\to}$v1 migration are ratified in the Binary Format companion (0.2.0 $\rightarrow$ 0.3.0). \\ \today & Schema major 1 Phase F (engrave ratify-only) & Ratifies three engrave/layout-ir dispositions the implementation already made (Pass 12 batch I8/I9/I10, now struck), with no byte-layout or architecture change: the \emph{break-constraint satisfaction} predicate (Requirement~\ref{req:layoutir:break-satisfaction} --- a \texttt{SystemBreakAt} / \texttt{PageBreakAt} is satisfied iff the final \texttt{ResolvedLayoutIR} starts a system/page at that slot, a region-first slot trivially); \emph{break-override attribution} threaded through a \texttt{ConstrainedLayoutIR.break\_origins} sidecar, declining to widen the normalized constraint record (Requirement~\ref{req:layoutir:break-origin-attribution}); and the \emph{system-continuation} synthesis kind \texttt{Registered(SYSTEM\_CONTINUATION\_SYNTHESIS)} with an \texttt{(original,\,ordinal)} instance key (Requirement~\ref{req:layoutir:continuation-synthesis}). Flags one open item: which region governs a cross-region slur's spanning permission (implemented as conservative AND, P12-K12). \\ \today & Pass 12 G-ratification (the batch pass) & Retires the open Pass-12 batch (28 rows; dispositions in \texttt{PASS12\_RATIFICATION\_LOG.md}). Algorithm dispositions: the spelling pre-pass and notational decomposition move to profile-declared-by-versioned- identifier with ratified v1 defaults (Requirements~\ref{req:pitch:spelling-algorithm} and~\ref{req:time:decomposition-algorithm}; closes P12-H1/H3/H4/H5/C5); decomposition precedence pinned fixed (P12-H6); authored annotations for inference-ineligible targets surface (Requirement~\ref{req:pitch:authored-uninferred}, P12-H7). Identity: system-derived intrinsic content immutable under reduction (P12-K3). Genesis ratified outside the operation set (P12-K8). Cross-region slur permission = AND (P12-K12). Re-anchoring: cue cascade-on-any-source, Range truncate defined, annotation orphaning sanctioned, \texttt{ReanchorReason:: SameCanvasNearer} appended (P12-C1/C2/C3/C4). Barriers: target-free and opaque-operation matching (Requirement~\ref{req:format:barrier-matching}, P12-E4); unsafe-edit tombstone semantics (Requirement~\ref{req:format:unsafe-tombstone}, P12-E5; encoding deferred to the Binary Format companion). Solver: kind-determined strength (Requirement~\ref{req:solver:kind-strength}, P12-I4), sub-conformant report shape (Requirement~\ref{req:solver:subconformant-report}, P12-I5), the Minimal-tier constraint floor (Requirement~\ref{req:layoutir:constraint-floor}, P12-I6). The stale \texttt{OperationKindTag} listing gains the eleven appended tags. Companion movements: Operation Catalog 0.5.0~$\rightarrow$~0.6.0, Binary Format 0.3.0~$\rightarrow$~0.4.0. \\ \today & Schema major 2 (data-model expansion, Phase A) & Defines the leaf types Chapter~5's ratified shapes referenced but never pinned: \texttt{SlurKind}, \texttt{CurveDirection}, \texttt{CurvatureOverride}, the shared \texttt{SpanStyle} record (consolidating the formerly-sketched identical \texttt{SlurStyle}/\texttt{TieStyle}/\texttt{SpannerStyle} triplet), \texttt{SubBeam}, \texttt{BeamGeometryOverride}, the \texttt{SpannerKind} payload types (\texttt{HairpinDirection}, \texttt{OctaveOffset}, \texttt{PedalKind}, \texttt{TextLineDefinition}, \texttt{BracketKind}) --- \texttt{SpannerKind} gains a leading \texttt{Generic} variant as the honest migration default --- \texttt{Volta}, \texttt{MetadataValue}, \texttt{Timestamp}, \texttt{SoundConfiguration}, \texttt{TranspositionInterval} (structural, advisory until the Chapter~4 tuning catalog per the P12-K2 discipline), \texttt{UnpitchedMember}, \texttt{SpaceUnit}, \texttt{LineStyle}, and \texttt{StaffBracketKind}. Ratifies the clef/key content model into Chapter~5 (\texttt{Clef}, \texttt{ClefShape}, \texttt{KeySignature}, \texttt{ClefChange}, \texttt{KeySignatureChange} --- the visible-slice value types) and retires the \texttt{ClefId} identifier sketch: \texttt{Staff.default\_clef} and \texttt{Instrument.default\_clef} embed \texttt{Clef} values, and \texttt{Instrument.default\_staff\_lines} is reconciled from \texttt{u8} to \texttt{StaffLineConfiguration}. Metadata timestamps are pinned strictly authored (Requirement~\ref{req:graph:metadata-timestamps}: nothing writes them implicitly). Chapter~8 gains the schema-major-2 paragraph (canonical base stays major~0; the filled values reach the snapshot and the seven embedding operation payloads, \texttt{InsertRegion} transitively). Wire forms and the total default-filling migration: Binary Format companion 0.4.0~$\rightarrow$~0.5.0. \\ \today & Schema major 2 (repeat authoring, Phase D) & The dedicated \texttt{CreateRepeatStructure}/% \texttt{DeleteRepeatStructure} pair enters the operation set: Chapter~6's \texttt{OperationKind} enum and Chapter~8's \texttt{OperationKindTag} listing gain the two variants (repeats are not \texttt{CrossCuttingValue} kinds on the wire --- the reconciliation note follows the \texttt{CrossCuttingStructure} listing), and the re-anchoring rule table gains the Repeat-structure~/ Anchor row (re-anchor to the nearest surviving anchor across \emph{every} event-referencing anchor site --- \texttt{start}/\texttt{end}, jump targets, volta spans --- cascade-delete only when none survives). Six-part schemas: Operation Catalog \sectionsc{Repeat Structures}, 0.6.0~$\rightarrow$~0.7.0. Wire discriminants 28/29 and the per-payload stamping (create born at v2; delete a minor kind append at major~0): Binary Format companion 0.5.0~$\rightarrow$~0.6.0. \\ \today & Schema major 2 (rendering, Phase F) & The rendering consumers of the schema-major-2 data model are ratified into Chapter~7. \texttt{ResolvedLayoutIR} gains the non-glyph primitive vocabulary it has always carried in implementation --- line \texttt{Stroke}s (staff lines, stems, barlines, volta brackets) and cubic-B\'ezier \texttt{Curve}s (slurs) --- as \ref{req:layoutir:resolved-primitives} (provenance-traced, non-canonical). Two Minimal-tier rendering floors join the constraint floor: \ref{req:layoutir:repeat-render} (E1: repeat barlines at resolved boundaries, volta brackets with ending numbers, honest non-drawing of unresolvable boundaries, jump-kind marks and cross-region repeats deferred) and \ref{req:layoutir:slur-curve} (E2: a slur renders as a cubic B\'ezier honoring \texttt{CurvatureOverride}, honest non-drawing of unresolvable or cross-staff spans, a non-\texttt{Solid} line style surfaced not silently rendered solid). The curvature-computing algorithm and the \texttt{RenderIR} encoding stay forward-referenced out (\ref{sec:layoutir:forward}, \ref{sec:layoutir:render}). Layout geometry is non-canonical, so no wire form and no companion version moves --- an engrave-side ratify-as-implemented, mirroring the schema-major-1 Phase~F precedent. \\ \today & Layout IR (slur rendering completion, Push 3) & The three slur refinements \ref{req:layoutir:slur-curve} deferred are now landed and its requirement extended: an authored non-\texttt{Solid} \texttt{SpanStyle} line renders faithfully (dashed / dotted --- the \texttt{LineStyle} rides the \texttt{Curve}, whose listing gains the field), a slur spanning a system break splits into per-system sub-curves (de~Casteljau; first segment keeps the slur's provenance, the rest synthesized continuations), and \texttt{slur\_shape\_penalty} moves off its pinned $0.0$ to a real measurement (Quality Metric Catalog \sectionsc{slur\_shape\_penalty} rationale refreshed; formula unchanged, no version move). Layout geometry stays non-canonical --- no wire form, no companion version moves. \\ \bottomrule \end{longtable} \end{document}