2111 lines
108 KiB
TeX
2111 lines
108 KiB
TeX
% !TEX program = xelatex
|
|
%
|
|
% Epiphany --- Operation Catalog (companion specification)
|
|
% Companion to the Core Specification. Compile with XeLaTeX.
|
|
%
|
|
% This document is versioned independently of the Core Specification
|
|
% (independent semver; see the Versioning note in the front matter). Its preamble
|
|
% is intentionally a self-contained copy of the core specification's preamble so
|
|
% the two documents build independently; factoring a shared preamble file is a
|
|
% later cleanup, not a v0.1 deliverable.
|
|
|
|
\documentclass[11pt,letterpaper]{report}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Packages
|
|
% ---------------------------------------------------------------------------
|
|
\usepackage{fontspec}
|
|
\usepackage{geometry}
|
|
\geometry{
|
|
letterpaper,
|
|
top=1.05in,
|
|
bottom=1.05in,
|
|
left=1.15in,
|
|
right=1.15in,
|
|
headheight=15pt
|
|
}
|
|
|
|
\usepackage[english]{babel}
|
|
\usepackage{microtype}
|
|
\usepackage{parskip}
|
|
\usepackage{xcolor}
|
|
\usepackage{hyperref}
|
|
\usepackage{enumitem}
|
|
\usepackage{titlesec}
|
|
\usepackage{fancyhdr}
|
|
\usepackage{booktabs}
|
|
\usepackage{array}
|
|
\usepackage{longtable}
|
|
\usepackage{listings}
|
|
\usepackage{amsmath}
|
|
\usepackage{amssymb}
|
|
\usepackage{tcolorbox}
|
|
\tcbuselibrary{breakable, skins}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Color palette (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\definecolor{epiphanyteal}{HTML}{1A4044}
|
|
\definecolor{epiphanygold}{HTML}{8E6E2E}
|
|
\definecolor{epiphanyink}{HTML}{1F1B16}
|
|
\definecolor{epiphanyslate}{HTML}{6B6660}
|
|
\definecolor{epiphanycream}{HTML}{F8F4ED}
|
|
\definecolor{epiphanymist}{HTML}{ECE8E0}
|
|
\definecolor{epiphanycode}{HTML}{2A2520}
|
|
\definecolor{epiphanycrimson}{HTML}{7A2424}
|
|
|
|
\hypersetup{
|
|
colorlinks=true,
|
|
linkcolor=epiphanyteal,
|
|
citecolor=epiphanyteal,
|
|
urlcolor=epiphanygold,
|
|
pdftitle={Epiphany --- Operation Catalog},
|
|
pdfauthor={The Epiphany Project},
|
|
pdfsubject={Operation Catalog companion for the Epiphany music notation platform},
|
|
pdfkeywords={music notation, operations, CRDT, reduction, serialization},
|
|
bookmarksnumbered=true,
|
|
bookmarksopen=true
|
|
}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Typography (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\setmainfont{TeX Gyre Pagella}[Numbers={OldStyle, Proportional}, Ligatures={TeX, Common}]
|
|
\setsansfont{TeX Gyre Heros}[Scale=0.94, Ligatures={TeX, Common}]
|
|
\setmonofont{TeX Gyre Cursor}[Scale=0.88, Ligatures={TeX}]
|
|
\newfontfamily\titlefont{TeX Gyre Pagella}[Numbers={OldStyle}, Ligatures={TeX, Common}]
|
|
\newcommand{\tablenums}[1]{{\addfontfeatures{Numbers={Lining,Tabular}}#1}}
|
|
\newcommand{\sectionsc}[1]{{\addfontfeatures{Letters=SmallCaps}#1}}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Section styling (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\titleformat{\chapter}[display]
|
|
{\normalfont\filright}
|
|
{\raggedright\color{epiphanygold}\fontsize{14pt}{16pt}\selectfont
|
|
\scshape Chapter\ \thechapter}
|
|
{16pt}
|
|
{\raggedright\color{epiphanyteal}\fontsize{32pt}{36pt}\selectfont\bfseries}
|
|
[\vspace{4pt}{\color{epiphanygold}\rule{2in}{0.6pt}}]
|
|
\titlespacing*{\chapter}{0pt}{-20pt}{30pt}
|
|
\titleformat{\section}
|
|
{\normalfont\Large\bfseries\color{epiphanyteal}}
|
|
{\color{epiphanygold}\thesection}{1em}{}
|
|
\titleformat{\subsection}
|
|
{\normalfont\large\bfseries\color{epiphanyteal}}
|
|
{\color{epiphanygold}\thesubsection}{1em}{}
|
|
\titleformat{\subsubsection}
|
|
{\normalfont\normalsize\bfseries\color{epiphanyink}}
|
|
{\thesubsubsection}{1em}{}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Headers and footers (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\pagestyle{fancy}
|
|
\fancyhf{}
|
|
\renewcommand{\headrulewidth}{0pt}
|
|
\renewcommand{\footrulewidth}{0pt}
|
|
\fancyhead[L]{\small\scshape\color{epiphanyslate}Epiphany --- Operation Catalog}
|
|
\fancyhead[R]{\small\itshape\color{epiphanyslate}\leftmark}
|
|
\fancyfoot[C]{\small\color{epiphanyslate}\thepage}
|
|
\renewcommand{\headrule}{
|
|
\color{epiphanygold!50}\hrule width\headwidth height 0.4pt
|
|
\vspace{1pt}
|
|
\color{epiphanygold!30}\hrule width\headwidth height 0.2pt
|
|
}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Code listing style (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\lstdefinelanguage{Rust}{
|
|
keywords={fn,let,mut,pub,struct,enum,impl,trait,for,in,if,else,match,return,
|
|
use,mod,crate,self,Self,as,where,move,async,await,const,static,
|
|
ref,type,unsafe,extern,dyn,box,break,continue,loop,while},
|
|
keywordstyle=\color{epiphanyteal}\bfseries,
|
|
ndkeywords={i8,i16,i32,i64,i128,u8,u16,u32,u64,u128,f32,f64,bool,char,str,
|
|
String,Vec,Option,Result,Box,Rc,Arc,HashMap,BTreeMap,
|
|
NonZeroU16,NonZeroU32,NonZeroU64,Duration,Timestamp},
|
|
ndkeywordstyle=\color{epiphanygold}\bfseries,
|
|
sensitive=true,
|
|
comment=[l]{//},
|
|
morecomment=[s]{/*}{*/},
|
|
commentstyle=\color{epiphanyslate}\itshape,
|
|
stringstyle=\color{epiphanycrimson},
|
|
morestring=[b]",
|
|
morestring=[b]'
|
|
}
|
|
\lstset{
|
|
basicstyle=\ttfamily\small\color{epiphanycode},
|
|
backgroundcolor=\color{epiphanycream},
|
|
frame=leftline,
|
|
rulecolor=\color{epiphanygold!60},
|
|
framesep=8pt,
|
|
framerule=1.5pt,
|
|
xleftmargin=10pt,
|
|
xrightmargin=4pt,
|
|
breaklines=true,
|
|
showstringspaces=false,
|
|
numberstyle=\tiny\color{epiphanyslate},
|
|
numbersep=10pt,
|
|
captionpos=b,
|
|
aboveskip=10pt,
|
|
belowskip=10pt,
|
|
language=Rust
|
|
}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Custom environments (shared with the core specification)
|
|
% ---------------------------------------------------------------------------
|
|
\newtcolorbox{openquestion}[1][]{
|
|
enhanced, breakable,
|
|
colback=epiphanymist, colframe=epiphanycrimson,
|
|
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Open Question},
|
|
coltitle=white, colbacktitle=epiphanycrimson,
|
|
arc=1pt, boxrule=0pt, leftrule=2pt,
|
|
left=10pt, right=10pt, top=8pt, bottom=8pt,
|
|
attach boxed title to top left={xshift=0pt, yshift=0pt},
|
|
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
|
|
#1
|
|
}
|
|
\newtcolorbox{rationale}[1][]{
|
|
enhanced, breakable,
|
|
colback=epiphanymist, colframe=epiphanyteal,
|
|
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Rationale},
|
|
coltitle=white, colbacktitle=epiphanyteal,
|
|
arc=1pt, boxrule=0pt, leftrule=2pt,
|
|
left=10pt, right=10pt, top=8pt, bottom=8pt,
|
|
attach boxed title to top left={xshift=0pt, yshift=0pt},
|
|
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
|
|
#1
|
|
}
|
|
% Numbered within chapter (this document has chapters); see core_spec.tex's
|
|
% requirement box for why a plain counter + `code=` step is used instead of
|
|
% tcolorbox's own "auto counter, number within=..." keys.
|
|
\newcounter{requirement}[chapter]
|
|
\renewcommand{\therequirement}{\thechapter.\arabic{requirement}}
|
|
\newtcolorbox{requirement}[1][]{
|
|
enhanced, breakable,
|
|
colback=white, colframe=epiphanygold,
|
|
fonttitle=\bfseries\color{white}, code={\refstepcounter{requirement}}, title={\scshape\hspace{2pt}Requirement~\therequirement},
|
|
coltitle=white, colbacktitle=epiphanygold,
|
|
arc=1pt, boxrule=0pt, leftrule=2pt,
|
|
left=10pt, right=10pt, top=8pt, bottom=8pt,
|
|
attach boxed title to top left={xshift=0pt, yshift=0pt},
|
|
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
|
|
#1
|
|
}
|
|
\newtcolorbox{nongoal}[1][]{
|
|
enhanced, breakable,
|
|
colback=epiphanymist, colframe=epiphanyslate,
|
|
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Non-Goal},
|
|
coltitle=white, colbacktitle=epiphanyslate,
|
|
arc=1pt, boxrule=0pt, leftrule=2pt,
|
|
left=10pt, right=10pt, top=8pt, bottom=8pt,
|
|
attach boxed title to top left={xshift=0pt, yshift=0pt},
|
|
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
|
|
#1
|
|
}
|
|
|
|
\newcommand{\MUST}{\textbf{MUST}}
|
|
\newcommand{\MUSTNOT}{\textbf{MUST}\nobreak\ \textbf{NOT}}
|
|
\newcommand{\SHOULD}{\textbf{SHOULD}}
|
|
\newcommand{\SHOULDNOT}{\textbf{SHOULD}\nobreak\ \textbf{NOT}}
|
|
\newcommand{\MAY}{\textbf{MAY}}
|
|
|
|
\setlist[itemize]{topsep=2pt, itemsep=3pt, parsep=0pt}
|
|
\setlist[enumerate]{topsep=2pt, itemsep=3pt, parsep=0pt}
|
|
\setlist[description]{topsep=2pt, itemsep=5pt, parsep=0pt}
|
|
\AtBeginDocument{\color{epiphanyink}}
|
|
|
|
% ---------------------------------------------------------------------------
|
|
% Document
|
|
% ---------------------------------------------------------------------------
|
|
\begin{document}
|
|
|
|
\begin{titlepage}
|
|
\thispagestyle{empty}
|
|
\centering
|
|
\vspace*{2.2in}
|
|
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[18pt]
|
|
{\titlefont\fontsize{34pt}{38pt}\selectfont\color{epiphanyteal}\bfseries Epiphany}\\[10pt]
|
|
{\Large\scshape\color{epiphanyslate}Operation Catalog}\\[6pt]
|
|
{\large\itshape\color{epiphanyslate}A companion to the Core Specification}\\[14pt]
|
|
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[24pt]
|
|
{\normalsize\color{epiphanyink}Version 0.12.0 --- The genesis operation tranche completes the root-level entity mints (G3a)}\\[4pt]
|
|
{\small\color{epiphanyslate}Normative for the operation kinds it defines}
|
|
\vfill
|
|
\end{titlepage}
|
|
|
|
\tableofcontents
|
|
|
|
% ===========================================================================
|
|
\chapter{About This Companion}
|
|
\label{ch:about}
|
|
|
|
The \emph{Operation Catalog} is a companion to the Epiphany Core Specification.
|
|
It fulfils the open question the core specification raises in its
|
|
\emph{Operation Catalog Conformance} section (Chapter~6,
|
|
\sectionsc{Semantic Operations and Concurrent Reduction}, the
|
|
\texttt{sec:semops:catalog} open question), which states that the catalog
|
|
``is normative once published; this specification is non-final until the catalog
|
|
is delivered.'' This release delivers the catalog \emph{framework}, the
|
|
\textbf{K0 representative primitive set}, and the \textbf{M2 broad-K0 groups}
|
|
(the event/pitch leaf-field, cross-cutting CRUD, structural-container, and
|
|
score-settings operations the Phase~2 slice exercises) --- the operation kinds
|
|
the Phase~2 visible slice and binary format actually exercise, each fully
|
|
specified in Chapter~\ref{ch:k0}. The remaining items of the full
|
|
$60$--$80$-primitive catalog are drafted as framework slots
|
|
(Chapter~\ref{ch:k1}) and completed in Phase~3.
|
|
|
|
\section{Relationship to the Core Specification}
|
|
|
|
This companion does not restate the operation framework; it \emph{references} it.
|
|
The framework --- operation identity and stamps, the hybrid-logical-clock
|
|
monotonicity rule, the dotted-version-vector causal context, the
|
|
order-independent operation-slot model and equivocation, the canonical reduction
|
|
order, the four-phase lifecycle, conflict records and the conflict registry,
|
|
re-anchoring, transactions, forward undo, and the LWW discipline --- is the core
|
|
specification's Chapter~6. This companion defines, for each operation kind, the
|
|
\emph{payload schema} and how that kind \emph{instantiates} the framework's
|
|
reduction, conflict, undo, and re-anchoring rules.
|
|
|
|
It also consumes, rather than re-deriving, the \textbf{ratified byte-convention
|
|
baseline} (core specification Chapter~8, \sectionsc{Binary Format Companion},
|
|
requirement \texttt{req:format:codec-conventions}, and the
|
|
\sectionsc{Canonical Byte-Layout Reference} appendix): little-endian integers, a
|
|
single discriminant byte per tagged union, \texttt{u32} length prefixes on every
|
|
variable-width leaf, and raw UTF-8 free text. Operation-payload encodings are
|
|
expressed in terms of that baseline; the literal wire layout of each payload is
|
|
the Binary Format companion's (Agent~J's) to pin, in coordination with this
|
|
catalog.
|
|
|
|
\begin{rationale}
|
|
The catalog is versioned \emph{independently} of the core specification
|
|
(independent semver). Operation kinds are added over time; the catalog should
|
|
evolve --- adding primitives, refining conflict cases --- without forcing a core
|
|
specification revision. The core specification changes only when the
|
|
\emph{framework} changes.
|
|
\end{rationale}
|
|
|
|
\section{Conformance Profiles}
|
|
|
|
A \textbf{Phase-2 profile} implementation \MUST{} implement every primitive in
|
|
Chapter~\ref{ch:k0} --- the representative set and the M2 broad-K0 groups --- with
|
|
the schema, reduction rule, conflict cases, undo semantics, and re-anchoring
|
|
behaviour defined there. The former create-score/create-canvas framework slots
|
|
of Chapter~\ref{ch:k1} are \emph{retired} (Pass~12): they are permanently
|
|
outside the operation set, so no operation kind exists to reject.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.6.0 (Pass-12 G-ratification).} No payload byte
|
|
changes. Ratified: ResolveConflict beyond the concurrent case (P12-K4),
|
|
ResolveEquivocation edge semantics (P12-K6) and the profile-policy deferral
|
|
(P12-K5), the RespellPitch migration fallback as long-term (P12-K1), undo
|
|
strand-block conflict-kind reuse (P12-K10) and the idempotence asymmetry
|
|
(P12-K11), the Transpose prototype pin (P12-K2 deferral), the cross-region
|
|
slur AND advisory (P12-K12), and the create-score/canvas retirement (P12-K8).
|
|
Appended vocabulary (minor, append-only):
|
|
\texttt{SystemDerivedContentImmutable} (12, P12-K3) and
|
|
\texttt{RecreateContentMismatch} (13, P12-K9) in
|
|
\texttt{PreconditionFailureReason}.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.8.0 (Push~4a, transpose algebra).} Adds one
|
|
operation kind, \texttt{TransposeInterval}
|
|
(Section~\ref{sec:k0:transpose-interval}), carrying the existing
|
|
\texttt{TranspositionInterval} of core Chapter~2. It transposes scale
|
|
position, refuses
|
|
atomically rather than saturating, takes a genuine \emph{set} of targets, and
|
|
propagates the resulting spelling. \texttt{Transpose}
|
|
(Section~\ref{sec:k0:transpose}) is \textbf{frozen}: its saturation, its
|
|
nominal-blindness, and its duplicate-sensitivity are pinned as normative
|
|
replay semantics and no longer authored. This supersedes the 0.6.0 pin of the
|
|
Transpose prototype (P12-K2), which anticipated a payload schema-major landing
|
|
with the tuning catalog; neither proved necessary. Appended vocabulary (minor,
|
|
append-only): \texttt{AcousticRealizationPinned} (14) and
|
|
\texttt{TranspositionOutOfRange} (15) in \texttt{PreconditionFailureReason};
|
|
\texttt{PitchSpaceMismatch} (6) is un-reserved and now produced for a
|
|
non-\texttt{Cmn} position. That case reads the position discriminant and does
|
|
not require a pitch-space registry.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.9.0 (P13-S2, space-relative CMN).} Broadens the
|
|
\texttt{PitchSpaceMismatch} (6) case for \texttt{TransposeInterval}: a
|
|
\texttt{Cmn} target whose enclosing chromatic structure or nominal mapping
|
|
cannot be established now refuses under core requirement
|
|
\texttt{req:pitch:space-capability-refusal}, rather than receiving guessed
|
|
12-chromatic arithmetic. This capability check is distinct from the 0.8.0
|
|
non-\texttt{Cmn} discriminant check and is replaced by structural registry
|
|
resolution in Push~4b. No payload bytes change and no
|
|
\texttt{PreconditionFailureReason} is appended; assignments 10 through 15
|
|
remain exactly as ratified.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.10.0, first half (genesis tranche G1,
|
|
retroactively recorded).} \texttt{CreateInstrument}
|
|
(Section~\ref{sec:k0:create-instrument}) landed at commit \texttt{3b09595}
|
|
without a version bump or a changelog paragraph, against this document's own
|
|
convention above --- an omission genesis tranche G2a repairs here rather than
|
|
silently. Adds one operation kind, \texttt{CreateInstrument}, mint-only
|
|
(set-union creation, no delete in this catalogue revision) on the score root,
|
|
carrying the full \texttt{Instrument} value. It is the single missing link
|
|
between an empty score (\texttt{Score::empty}) and a note: \texttt{CreateStaff}
|
|
already demands a live \texttt{Instrument}, and nothing else could mint one.
|
|
Schema major~2 unconditionally (\texttt{Instrument}'s major-2 appends are
|
|
mandatory fields, not \texttt{Option}-hidden).
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.10.0, second half (genesis tranche G2a).} Adds two
|
|
operation kinds, \texttt{SetCanvasLayoutDefaults}
|
|
(Section~\ref{sec:k0:set-canvas-layout-defaults}) and
|
|
\texttt{SetSpellingPrecedence} (Section~\ref{sec:k0:set-spelling-precedence}),
|
|
on exactly the \texttt{SetMetadata} advisory-LWW pattern
|
|
(Section~\ref{sec:k0:score-settings}): the canvas's default layout advisories
|
|
and the score's spelling-precedence configuration join the operation-authored
|
|
surfaces. Both are schema major~0 unconditionally --- neither carried type's
|
|
canonical encoding, taken as a standalone payload, has ever been versioned.
|
|
No accept-set change: the third genesis-ladder setter,
|
|
\texttt{SetTuningContext}, is schema major~3 and is deliberately deferred to
|
|
its own rung (G2b) rather than anticipated here.
|
|
|
|
The \emph{Value restoration} passage
|
|
(Section~\ref{sec:k0:undo}) and the \emph{Retired slots} closing note both
|
|
gain a repair alongside this addition: the former's LWW-family enumeration
|
|
now names both new setters, and the latter's ``outside the operation set''
|
|
clause is narrowed to the canvas object itself, per
|
|
\texttt{spec/RULING\_GENESIS\_PERSISTENCE.md} --- the score's \emph{contents}
|
|
are no longer outside the operation set; the canvas is still never
|
|
op-minted.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.11.0 (genesis tranche G2b,
|
|
\texttt{spec/CONTRACT\_GENESIS\_G2B\_TUNING.md}).} Adds one operation kind,
|
|
\texttt{SetTuningContext} (Section~\ref{sec:k0:set-tuning-context}), the
|
|
third and final genesis-ladder setter deferred at 0.10.0. Carries
|
|
\texttt{TuningContextSettings}, the authored subset of
|
|
\texttt{ScoreTuningContext} --- exactly its five wire-bearing fields, byte-
|
|
identical to the existing schema-major-3 wire walk (Binary Format companion
|
|
\sectionsc{Schema Major 3}) --- rather than the full graph type, so that
|
|
\texttt{accidental\_extensions} (staged off the wire) cannot diverge between
|
|
a live-authored session and the same document reloaded from bytes.
|
|
|
|
\textbf{The sole accept-set-raising surface among the nine genesis
|
|
settings/creates.} \texttt{SetTuningContext} is schema major~3
|
|
\emph{unconditionally} (\texttt{ScoreTuningContext}'s \texttt{smufl}/
|
|
\texttt{overrides} appends are mandatory, so no lower-major layout for this
|
|
payload exists at any value), which is why it alone --- not
|
|
\texttt{CreateInstrument} at major~2, and not either G2a setter at major~0 ---
|
|
drags the \texttt{OperationEnvelopeBlock} accept-set from \texttt{[0, 2]} to
|
|
\texttt{[0, 3]} (Binary Format companion \sectionsc{The Chunk-Level Gate}).
|
|
This is the reason the genesis ladder split \texttt{SetTuningContext} into
|
|
its own rung rather than landing it alongside \texttt{SetCanvasLayoutDefaults}
|
|
/ \texttt{SetSpellingPrecedence}: a one-way accept-set door is not something
|
|
to bury inside two unrelated major-0 setters.
|
|
|
|
Undo restores the \textbf{seeded base} settings --- default or non-default
|
|
alike --- exactly as \texttt{SetMetadata} does; the never-authored /
|
|
authored-to-default distinction stays unobservable
|
|
(Section~\ref{sec:k0:set-tuning-context}). This closes P13-S13: the
|
|
canonical operation log is now the sole canonical carrier of the tuning
|
|
context (the canonical base embeds no \texttt{Score} field value for any
|
|
setting, \texttt{metadata} included), on the same reasoning that already
|
|
applied to \texttt{SetMetadata}.
|
|
|
|
\medskip
|
|
|
|
\noindent\textbf{Version 0.12.0 (genesis tranche G3a,
|
|
\texttt{spec/CONTRACT\_GENESIS\_G3A\_ENTITIES.md}).} Adds four operation
|
|
kinds --- \texttt{CreateStaffGroup} (Section~\ref{sec:k0:create-staff-group}),
|
|
\texttt{CreatePartDefinition} (Section~\ref{sec:k0:create-part-definition}),
|
|
\texttt{CreateAnalysisLayer} (Section~\ref{sec:k0:create-analysis-layer}),
|
|
\texttt{CreateView} (Section~\ref{sec:k0:create-view}) --- completing the
|
|
root-level \texttt{Score} entity mints: every remaining vector a Chapter~5
|
|
top-level structure declares (\texttt{staff\_groups}, \texttt{parts},
|
|
\texttt{analysis\_layers}, \texttt{views}) is now operation-authored. All
|
|
four are mint-only (set-union creation, no delete in this catalogue
|
|
revision), schema major~0 unconditionally, epoch~11.
|
|
|
|
\textbf{The defect this closes, and what it does not close.} Until this
|
|
rung, all four object kinds became live \emph{only} through canonical-base
|
|
ingest; no operation minted any of them. Consequently \texttt{CreateStaff}'s
|
|
\texttt{group} precondition (Section~\ref{sec:k0:create-staff}) was
|
|
\textbf{unsatisfiable} under from-empty reduction --- a document built only
|
|
from operations could never author a grouped staff. G3a closes exactly this
|
|
satisfiability half. It does \textbf{not} make a bidirectionally consistent
|
|
staff group authorable: with mints only, and no modify or delete operation
|
|
for either side, no authoring order can bring \texttt{Staff.group} and
|
|
\texttt{StaffGroup.members} into agreement once they disagree.
|
|
|
|
\textbf{Ruled (disposition B, filed as P13-S16): \texttt{Staff.group} is the
|
|
sole authority for group membership; \texttt{StaffGroup.members} is a
|
|
non-authoritative denormalized projection.} \texttt{CreateStaffGroup} stores
|
|
\texttt{members} exactly as carried and neither maintains nor trusts it
|
|
thereafter. Both stale forms are permitted outcomes, not defects: a
|
|
\textbf{missing} member (\texttt{Staff.group} names a group whose
|
|
\texttt{members} omits that staff) and a \textbf{spurious} member
|
|
(\texttt{StaffGroup.members} names a staff whose own \texttt{Staff.group} is
|
|
absent or names a different group). Every consumer \MUST{} read group
|
|
membership from \texttt{Staff.group} only; core specification Chapter~5's
|
|
\texttt{Staff}/\texttt{StaffGroup} declarations (\sectionsc{Top-Level Score
|
|
Structure}) now state this normatively, and Section~\ref{sec:k0:create-staff}
|
|
above states the corresponding authoring-order consequence for
|
|
\texttt{CreateStaff}. Disposition A (maintaining \texttt{members} by
|
|
reduction, making it a derived field) remains the later
|
|
maintenance/enforcement fix, sequenced after G3b.
|
|
|
|
\textbf{Referential preconditions, graph-aware only, mirroring
|
|
\texttt{CreateStaff} exactly.} \texttt{CreateStaffGroup.members} and
|
|
\texttt{CreatePartDefinition.staves} each precondition every named id
|
|
resolves to a live \texttt{Staff}; \texttt{CreateView.active\_layers}
|
|
preconditions every named id resolves to a live analysis layer;
|
|
\texttt{CreateAnalysisLayer} carries no outbound reference and needs no
|
|
referential precondition at all, the \texttt{CreateInstrument} shape rather
|
|
than the \texttt{CreateStaff} shape. Base-free reduction enforces none of
|
|
these, exactly as for \texttt{CreateStaff}: there is no universe to check
|
|
against. No new \texttt{PreconditionFailureReason} --- every referential
|
|
refusal reuses \texttt{TargetMissing} (discriminant 0).
|
|
|
|
No accept-set change: all four carried types have exactly one wire layout
|
|
(core specification's \sectionsc{Binary Format Companion} confirms no
|
|
versioned walk exists for any of the four), so none gains a
|
|
\texttt{schema\_major()} arm beyond the existing catch-all.
|
|
|
|
% ===========================================================================
|
|
\chapter{The Catalog Framework}
|
|
\label{ch:framework}
|
|
|
|
\section{Value-Typed Payloads}
|
|
\label{sec:framework:value-typed}
|
|
|
|
Every operation payload in this catalog is \textbf{value-typed}: it carries the
|
|
real graph values its effect introduces, not identifiers that point at values in
|
|
some ambient graph. An \texttt{InsertEvent} carries the whole \texttt{Event}; a
|
|
\texttt{RespellPitch} carries the whole \texttt{PitchSpelling}; a
|
|
\texttt{CreateCrossCutting} carries the whole tie, slur, beam, or spanner.
|
|
|
|
\begin{rationale}
|
|
The v0 prototype carried \emph{identifier-only projections} --- an
|
|
\texttt{InsertEvent} held an \texttt{EventId} plus reduction-relevant scalars; a
|
|
\texttt{RespellPitch} held a content-hash \emph{fingerprint} of the new spelling.
|
|
That was sufficient to make an envelope hashable and to drive reduction, but it
|
|
is \textbf{not durable}: an operation replayed in a fresh context --- a backup
|
|
restore, a cross-tool round-trip --- needs the full value, which an
|
|
identifier-only payload cannot supply. Value-typed payloads are what make an
|
|
Epiphany document portable.
|
|
\end{rationale}
|
|
|
|
\begin{requirement}
|
|
\label{req:catalog:value-encoding}
|
|
A value-typed payload field \MUST{} be encoded by emitting the field's value
|
|
under the core specification's canonical value encoding
|
|
(\texttt{req:format:codec-conventions}), framed by a \texttt{u32} little-endian
|
|
length prefix. Decoding \MUST{} be the exact inverse and \MUST{} reject trailing
|
|
bytes within the framed region. The encoding introduces no byte layout beyond the
|
|
ratified baseline: a value's bytes here are byte-for-byte the bytes the whole
|
|
document codec emits for that value.
|
|
\end{requirement}
|
|
|
|
\section{Per-Primitive Schema Template}
|
|
\label{sec:framework:template}
|
|
|
|
Each catalog primitive is specified under a fixed six-part template. A new
|
|
primitive is a schema-fill against this template, not a fresh design:
|
|
|
|
\begin{description}
|
|
\item[Payload schema] The value-typed fields the operation carries, with their
|
|
core-specification types.
|
|
\item[Canonical encoding] The field order and framing, consuming
|
|
Requirement~\ref{req:catalog:value-encoding}. (The literal byte layout is the
|
|
Binary Format companion's; this catalog fixes the \emph{field set and order}.)
|
|
\item[Reduction rule] How the operation mutates canonical state when it is
|
|
reached in canonical reduction order --- the preconditions it checks, the
|
|
objects it mints or tombstones, and the bookkeeping it records.
|
|
\item[Conflict cases] The conflict records the operation can produce, by
|
|
\texttt{ConflictKind}, and which participant materialises.
|
|
\item[Undo semantics] The compensating effect of undoing a transaction that
|
|
contains the operation, under each \texttt{UndoPolicy}
|
|
(\texttt{StrictInverse} / \texttt{BestEffort} / \texttt{Cascade}).
|
|
\item[Re-anchoring] The behaviour when an object the operation references is
|
|
tombstoned before or concurrently with it.
|
|
\end{description}
|
|
|
|
\section{Reduction-Discipline Coverage}
|
|
|
|
The K0 representative set is chosen so that, between them, the primitives
|
|
exercise \emph{every} reduction discipline the framework defines:
|
|
position-keyed insert with system-voice promotion; delete-wins with tombstones,
|
|
tuplet compensation, and cross-cutting re-anchoring; field-overwrite with
|
|
last-writer-wins and structural-field-collision conflicts; set-union creation;
|
|
structural time-model migration; an LWW advisory; atomic transactions with
|
|
descriptor precedence; and the two meta-operations (conflict resolution and
|
|
forward undo). A primitive added later that reuses one of these disciplines
|
|
inherits its reduction, conflict, undo, and re-anchoring treatment.
|
|
|
|
% ===========================================================================
|
|
\chapter{K0 --- Representative Primitives}
|
|
\label{ch:k0}
|
|
|
|
This chapter is normative under the Phase-2 profile. Each primitive's reduction,
|
|
conflict, undo, and re-anchoring behaviour is the behaviour the core
|
|
specification's Chapter~6 defines for its discipline; the description here states
|
|
how the primitive instantiates it. The reference implementation is
|
|
\texttt{epiphany-ops} (the \texttt{payload}, \texttt{reduce}, and \texttt{migrate}
|
|
modules).
|
|
|
|
\section{InsertEvent}
|
|
\label{sec:k0:insert-event}
|
|
|
|
\textbf{Payload schema.} \texttt{InsertEventOp \{ staff\_instance:
|
|
StaffInstanceId, event: Event \}}. The voice, region-local position, duration,
|
|
and pitch identities are read from the \texttt{Event} value; the
|
|
\texttt{staff\_instance} is retained alongside it so the system-voice promotion
|
|
derivation is total without a containment walk.
|
|
|
|
\textbf{Canonical encoding.} \texttt{staff\_instance}, then the length-framed
|
|
canonical bytes of \texttt{event}.
|
|
|
|
\textbf{Reduction rule.} A position-keyed insert. Preconditions: the event's
|
|
duration is positive; the event id is neither live nor tombstoned; in graph-aware
|
|
reduction the target voice exists in a metric region and the event's pitch ids
|
|
are fresh. The event and its pitches are minted live; the voice is created on
|
|
first use. Concurrent inserts whose half-open duration intervals overlap in the
|
|
same voice are resolved by an order-independent promotion pre-pass: the
|
|
lower-\texttt{OperationId} insert is retained in the original voice, and each
|
|
overlapping loser is promoted to the deterministic system voice
|
|
\texttt{derive\_promoted\_voice\_id(staff\_instance, voice, winner, loser)} and
|
|
tagged \texttt{VoicePromoted}.
|
|
|
|
\textbf{Conflict cases.} None at reduction time; promotion is a deterministic
|
|
repair, not a conflict.
|
|
|
|
\textbf{Undo semantics.} Undoing the enclosing transaction tombstones the minted
|
|
event, its pitches, and any promoted voice. \texttt{StrictInverse} conflicts if
|
|
any was already tombstoned or modified; \texttt{BestEffort} tombstones the
|
|
survivors; \texttt{Cascade} is \texttt{StrictInverse} over the same minted set
|
|
(dependent-closure undo is a Phase-3 refinement).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (the operation mints, it does not
|
|
reference a pre-existing object that could be tombstoned).
|
|
|
|
\section{DeleteEvent}
|
|
\label{sec:k0:delete-event}
|
|
|
|
\textbf{Payload schema.} \texttt{DeleteEventOp \{ event: EventId,
|
|
tuplet\_compensation: TupletCompensation \}}, where \texttt{TupletCompensation}
|
|
is \texttt{NotInTuplet}, \texttt{ReplaceWithRest \{ rest: Rest \}} (value-typed
|
|
replacement rest), \texttt{RewriteTuplets \{ tuplets \}}, or
|
|
\texttt{CascadeDeleteTuplets \{ tuplets \}}.
|
|
|
|
\textbf{Canonical encoding.} \texttt{event}, then the tuplet-compensation
|
|
discriminant and its payload (a length-framed \texttt{Rest} value for
|
|
\texttt{ReplaceWithRest}).
|
|
|
|
\textbf{Reduction rule.} Delete-wins: the event and its contained pitches are
|
|
tombstoned (retaining their identifiers). Tuplet compensation, when present, adds
|
|
the replacement rest live or tombstones the cascaded tuplet group. Concurrent
|
|
deletes of the same event are idempotent.
|
|
|
|
\textbf{Conflict cases.} None for the delete itself. Graph-aware reduction
|
|
refuses an ill-formed tuplet compensation as a precondition failure (no-op).
|
|
|
|
\textbf{Undo semantics.} An insert-shaped compensation re-introduces the
|
|
tombstoned content; for the prototype's minted-object model this is the inverse
|
|
of the tombstone set, with the policy treatment described under InsertEvent.
|
|
|
|
\textbf{Re-anchoring.} Tombstoning the event runs the framework's re-anchoring
|
|
rule table over every cross-cutting structure that referenced it: a tie
|
|
cascade-deletes; a comment or analytical annotation orphans (user content is
|
|
never silently deleted); a beam truncates while $\geq 2$ members survive and
|
|
otherwise cascade-deletes; a slur or spanner re-anchors to the nearest surviving
|
|
endpoint while $\geq 1$ survives and otherwise cascade-deletes.
|
|
|
|
\section{RespellPitch}
|
|
\label{sec:k0:respell-pitch}
|
|
|
|
\textbf{Payload schema.} \texttt{RespellPitchOp \{ pitch: PitchId, spelling:
|
|
PitchSpelling \}} --- the full spelling value (v1), not a fingerprint.
|
|
|
|
\textbf{Canonical encoding.} \texttt{pitch}, then the length-framed canonical
|
|
bytes of \texttt{spelling}.
|
|
|
|
\textbf{Reduction rule.} A last-writer-wins field overwrite, keyed by pitch.
|
|
Precondition: the pitch is live. The resolved spelling is the one carried by the
|
|
operation latest in canonical order. Two respellings of one pitch that are
|
|
causally ordered overwrite intentionally; two \emph{concurrent} respellings with
|
|
\emph{equal} spelling reduce idempotently.
|
|
|
|
\textbf{Conflict cases.} Two concurrent respellings of one pitch with
|
|
\emph{differing} spellings produce a \texttt{StructuralFieldCollision} conflict
|
|
recording the winner (later in canonical order), the loser, and the field
|
|
\texttt{spelling}. The winner materialises and carries the \texttt{Conflicted}
|
|
effect tag.
|
|
|
|
\textbf{Undo semantics.} Value-restoring (Section~\ref{sec:k0:undo}): undo
|
|
restores the pre-operation spelling (or removes the spelling if the operation
|
|
introduced the first one), under the active policy.
|
|
|
|
\textbf{Re-anchoring.} If the target pitch is tombstoned, the respelling is a
|
|
no-op (\texttt{TargetTombstoned}).
|
|
|
|
\emph{Migration (ratified Pass~12, closing P12-K1).} A v0
|
|
\texttt{RespellPitch} carried only a content-hash \emph{fingerprint} of the
|
|
spelling. The fingerprint cannot be inverted to a \texttt{PitchSpelling}
|
|
without a side table, so the v0$\rightarrow$v1 migration
|
|
(Chapter~\ref{ch:migration}) recovers the spelling from the score graph
|
|
context --- an explicit per-pitch spelling attachment whose canonical bytes
|
|
hash to the fingerprint --- and, when the context lacks it, declares the
|
|
envelope unmigratable (the bundle opens read-only). This read-only fallback
|
|
is the \emph{long-term} disposition: no richer v0 corpus is or will be
|
|
required (no production v0 corpus exists). It remains the one representative
|
|
payload that is not self-contained under migration.
|
|
|
|
\section{ModifyEvent}
|
|
\label{sec:k0:modify-event}
|
|
|
|
\textbf{Payload schema.} \texttt{ModifyEventOp \{ event: Event \}} --- the full
|
|
replacement \texttt{Event} value (v1). The identity (and therefore the LWW key)
|
|
is read from the value.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of \texttt{event}.
|
|
|
|
\textbf{Reduction rule.} A last-writer-wins field overwrite keyed by event id.
|
|
Precondition: the event is live. The resolved value is the one carried by the
|
|
operation latest in canonical order; two causally ordered modifications overwrite
|
|
intentionally, and two \emph{concurrent} modifications with \emph{equal} value
|
|
reduce idempotently. Graph-aware reduction overwrites the event in place, and a
|
|
modification that \emph{moves} a metric event (a different region-local
|
|
\texttt{Musical} position or duration) is \emph{materialised}: the owning voice
|
|
is re-sorted by ascending position (id-tiebroken --- the same order an insert
|
|
maintains), preserving \texttt{VoiceEventsSortedNonOverlap} (Chapter~5
|
|
invariant~3). To keep that invariant, a placement change carries a
|
|
\emph{placement precondition}, read from the reducer's canonical voice-occupancy
|
|
index (graph-independent, so graph-free and graph-aware reduction agree): a move
|
|
with a non-positive span, or one that would overlap another live event in the
|
|
voice, is refused as a clean precondition no-op (\texttt{EventDurationInvalid})
|
|
rather than skipped silently. A materialised move updates the occupancy index,
|
|
so a later insert sees the freed or changed span. A placement change of a
|
|
\emph{non-metric} event is recorded in the bookkeeping but not applied to the
|
|
graph (re-sorting a non-metric voice is a deferred refinement); a malformed
|
|
(empty pitched) replacement is likewise recorded but not materialised;
|
|
same-placement field edits apply in place, preserving voice membership. Partial
|
|
trimming of a tuplet member remains a later refinement.
|
|
|
|
\emph{System-derived content immutability (ratified Pass~12, closing
|
|
P12-K3).} A modification that would rewrite the \emph{intrinsic content} of a
|
|
pitch whose identifier lives in the \texttt{SYSTEM\_DERIVED} namespace is
|
|
refused as a clean precondition no-op with the appended reason
|
|
\texttt{SystemDerivedContentImmutable} (discriminant 12): the identifier is
|
|
content-derived, and an in-place rewrite would silently invalidate its
|
|
derivation (core specification, Chapter~5 system-derived identity). The same
|
|
precondition applies to \texttt{ModifyIdentifiedPitch}
|
|
(Section~\ref{sec:k0:identified-pitch}). The sanctioned path is minting a
|
|
replacement pitch.
|
|
|
|
\textbf{Conflict cases.} Two concurrent modifications of one event with
|
|
\emph{differing} values produce a \texttt{StructuralFieldCollision} on the field
|
|
\texttt{event}, recording the winner (later in canonical order) and the loser.
|
|
|
|
\textbf{Undo semantics.} Value-restoring (Section~\ref{sec:k0:undo}): undoing
|
|
the enclosing transaction restores the event's chain-predecessor value,
|
|
conflicting (\texttt{StrictInverse}) or skipping (\texttt{BestEffort}) when a
|
|
later modification has superseded it.
|
|
|
|
\textbf{Re-anchoring.} If the target event is tombstoned, the modification is a
|
|
no-op (\texttt{TargetTombstoned}/\texttt{TargetMissing}).
|
|
|
|
\section{Identified-Pitch Operations}
|
|
\label{sec:k0:identified-pitch}
|
|
|
|
\textbf{Payload schema.} \texttt{InsertIdentifiedPitchOp \{ event: EventId,
|
|
pitch: IdentifiedPitch \}} mints a pitch into a live event;
|
|
\texttt{DeleteIdentifiedPitchOp \{ pitch: PitchId \}} tombstones one;
|
|
\texttt{ModifyIdentifiedPitchOp \{ pitch: PitchId, value: Pitch \}} overwrites a
|
|
pitch's acoustic / scale-position value (distinct from \texttt{RespellPitch},
|
|
which overwrites only the \emph{spelling}).
|
|
|
|
\textbf{Canonical encoding.} Insert: \texttt{event}, then the length-framed
|
|
\texttt{pitch} value. Delete: \texttt{pitch}. Modify: \texttt{pitch}, then the
|
|
length-framed \texttt{value}.
|
|
|
|
\textbf{Reduction rule.} The pitch-level analogues of the event-level mint,
|
|
delete, and field overwrite, inheriting their disciplines
|
|
(Sections~\ref{sec:k0:insert-event}, \ref{sec:k0:delete-event}, and this
|
|
chapter's field-overwrite treatment). \textbf{A note and a rest are the same slot
|
|
under pitch add/remove} (normative): deleting the \emph{only} pitch of a
|
|
single-pitch note degrades the event to a \texttt{Rest} of the same
|
|
id/voice/position/duration rather than leaving an empty pitched event (Chapter~5
|
|
forbids the empty chord, \texttt{ArenaError::EmptyPitchedEvent}); inserting a
|
|
pitch into a rest is the dual, promoting it to a one-pitch note. This preserves
|
|
the delete-wins / mint disciplines and keeps the graph consistent with the
|
|
bookkeeping, which tombstones or mints the pitch object either way.
|
|
|
|
\textbf{Conflict cases.} Insert and delete: none (mint is set-union; delete-wins
|
|
is idempotent). Modify: two concurrent differing writes of one pitch produce a
|
|
\texttt{StructuralFieldCollision} on the field \texttt{pitch}.
|
|
|
|
\textbf{Undo semantics.} Insert: undoing the enclosing transaction tombstones
|
|
the minted pitch, re-resting the event if it was the only one
|
|
(Section~\ref{sec:k0:undo}). Modify: value-restoring
|
|
(Section~\ref{sec:k0:undo}) --- the pitch's chain-predecessor value is
|
|
restored unless superseded. Delete: still not inverted (re-introducing a
|
|
tombstoned pitch is the deferred resurrection case, P11-C8).
|
|
|
|
\textbf{Re-anchoring.} An operation whose target event or pitch is tombstoned is
|
|
a no-op; tombstoning a pitch runs the cross-cutting re-anchoring table over any
|
|
structure that referenced it (see DeleteEvent).
|
|
|
|
\section{Transpose (frozen; replay only)}
|
|
\label{sec:k0:transpose}
|
|
|
|
\begin{requirement}
|
|
\label{req:opcat:transpose-frozen}
|
|
\texttt{Transpose} is \textbf{frozen}. Its reduction rule is fixed at the
|
|
semantics described in this section, including the saturation and the
|
|
duplicate-sensitivity, for as long as any stored operation carries it. A
|
|
conforming implementation \MUST{} reduce it exactly as specified here and
|
|
\MUSTNOT{} emit it from new authoring; new transpositions are authored as
|
|
\texttt{TransposeInterval} (Section~\ref{sec:k0:transpose-interval}).
|
|
\end{requirement}
|
|
|
|
\textbf{Payload schema.} \texttt{TransposeOp \{ targets: Vec<PitchId>,
|
|
chromatic\_steps: i32 \}}. Pitch identifiers are preserved. The operation
|
|
rewrites \emph{scale position} (the \texttt{Cmn} alteration), not acoustic
|
|
content.
|
|
|
|
\textbf{Canonical encoding.} \texttt{targets} in ascending canonical-byte order,
|
|
then \texttt{chromatic\_steps} as a little-endian \texttt{i32}. The sequence is
|
|
\emph{sorted but not deduplicated}: it is a multiset on the wire.
|
|
|
|
\textbf{Reduction rule.} An order-dependent content overwrite. For each live,
|
|
non-system target \emph{occurrence} in \texttt{targets}, the target's \texttt{Cmn}
|
|
\texttt{alteration} is shifted by \texttt{chromatic\_steps} and clamped to the
|
|
\texttt{i8} bound. Consequences, all normative for replay:
|
|
|
|
\begin{itemize}
|
|
\item A target appearing $k$ times is shifted $k$ times.
|
|
\item The nominal and octave never change: transposing C4 by $+12$ yields C4
|
|
with \texttt{alteration} $= 12$, not C5.
|
|
\item A shift past the \texttt{i8} bound saturates silently, and the operation
|
|
still reports \texttt{Applied}. Transposition under this rule is therefore
|
|
not invertible.
|
|
\item A target whose position is not \texttt{Cmn} is left unchanged, and the
|
|
operation still reports \texttt{Applied}.
|
|
\end{itemize}
|
|
|
|
\begin{rationale}
|
|
Every bullet above is a defect (P12-K2), and none of them may be repaired in
|
|
place. An operation is history: a replica replaying a stored
|
|
\texttt{Transpose} must reconstruct the state its author saw, and a
|
|
``corrected'' reduction rule would silently rewrite every score that ever
|
|
used one. The defects are therefore \emph{pinned}, not fixed, and the repair
|
|
ships as a new operation kind whose discriminant no historical operation
|
|
carries. Ratified Push~4a; supersedes the Pass~12 pin, which anticipated a
|
|
payload schema-major and a dependency on the tuning catalog. Neither proved
|
|
necessary: transposition acts on scale position, and appending an operation
|
|
kind is a schema \emph{minor} (Binary Format companion, requirement
|
|
\texttt{req:binfmt:kind-discriminants}).
|
|
\end{rationale}
|
|
|
|
\textbf{Conflict cases.} None --- composition is deterministic in canonical order
|
|
(a deterministic repair, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Transpose mints nothing, so the prototype's minted-object
|
|
undo (Section~\ref{sec:k0:undo}) does not negate it, and it records no write into
|
|
the pitch's value chain, so \emph{value-restoring undo does not restore it
|
|
either}: undoing a transaction containing a \texttt{Transpose} leaves the shifted
|
|
pitch shifted. An inverse-interval undo does not exist (under saturation the
|
|
inverse is not a function) and remains a deferred refinement (P11-C8).
|
|
|
|
\textbf{This too is frozen.} Making \texttt{Transpose} record into the value
|
|
chain would not change its own reduction rule, but it \emph{would} change what a
|
|
stored history $\{\texttt{Transpose}, \texttt{UndoTransaction}\}$ replays to ---
|
|
from "the pitch stays shifted" to "the pitch returns" --- which is a change in
|
|
what an existing document means. \texttt{TransposeInterval} records its write and
|
|
\emph{is} undoable (Section~\ref{sec:k0:transpose-interval}); the frozen
|
|
operation is permanently not. That asymmetry is another reason never to author
|
|
it.
|
|
|
|
\textbf{Re-anchoring.} Tombstoned targets are skipped (the transpose applies only
|
|
to live pitches). \texttt{SYSTEM\_DERIVED}-namespace targets are likewise
|
|
\emph{skipped} (ratified Pass~12, P12-K3): their intrinsic content is immutable,
|
|
and an in-place alteration shift would desynchronize the content from the id's
|
|
derivation inputs. A transpose whose live targets are \emph{all} system-derived
|
|
reduces as a precondition no-op (\texttt{SystemDerivedContentImmutable}).
|
|
|
|
\section{TransposeInterval}
|
|
\label{sec:k0:transpose-interval}
|
|
|
|
\textbf{Payload schema.} \texttt{TransposeIntervalOp \{ targets:
|
|
CanonicalSet<PitchId>, interval: TranspositionInterval \}}, reusing the
|
|
diatonic/chromatic pair the core specification defines in Chapter~2,
|
|
\sectionsc{Transposition and the Interval Type} --- the same value an
|
|
\texttt{Instrument} carries as its written-versus-sounding interval. Pitch
|
|
identifiers are preserved; the operation rewrites scale position and leaves
|
|
acoustic realization untouched.
|
|
|
|
\begin{requirement}
|
|
\label{req:opcat:transpose-interval-targets}
|
|
\texttt{targets} is a \textbf{set}. It \MUST{} be encoded as a sequence in
|
|
ascending canonical-byte order with \emph{strictly increasing} elements; a
|
|
decoder \MUST{} reject a sequence containing a duplicate rather than
|
|
normalize it. A transposition is a function of \emph{which} pitches it names,
|
|
never of how many times it names them.
|
|
\end{requirement}
|
|
|
|
\textbf{Canonical encoding.} The strictly-increasing \texttt{targets} sequence,
|
|
then \texttt{interval.diatonic\_steps} and \texttt{interval.chromatic\_steps},
|
|
each a little-endian \texttt{i32}.
|
|
|
|
\textbf{Reduction rule.} An order-dependent content overwrite. Tombstoned and
|
|
\texttt{SYSTEM\_DERIVED} targets are skipped, exactly as for \texttt{Transpose};
|
|
if no mutable target remains the operation reduces as the corresponding
|
|
precondition no-op. Otherwise, every remaining target \MUST{} be transposable
|
|
under core specification requirement \texttt{req:pitch:transposition}, and each
|
|
is transposed by \texttt{interval}.
|
|
|
|
\begin{requirement}
|
|
\label{req:opcat:transpose-interval-atomic}
|
|
If any mutable target is \emph{not} transposable, the operation \MUST{}
|
|
reduce to a no-op that changes no pitch, reporting the precondition failure
|
|
of the first such target in canonical order. It \MUSTNOT{} transpose the
|
|
remaining targets. The four cases and their
|
|
\texttt{PreconditionFailureReason} discriminants:
|
|
\begin{itemize}
|
|
\item a non-\texttt{Cmn} \texttt{scale\_position.position} $\Rightarrow$
|
|
\texttt{PitchSpaceMismatch} (6);
|
|
\item a \texttt{Cmn} position whose enclosing chromatic structure or
|
|
nominal mapping cannot be established $\Rightarrow$
|
|
\texttt{PitchSpaceMismatch} (6);
|
|
\item an \texttt{AcousticRealization::AbsoluteHz} realization
|
|
$\Rightarrow$ \texttt{AcousticRealizationPinned} (14);
|
|
\item an \texttt{alteration}$'$ or \texttt{octave}$'$ that does not fit
|
|
its field $\Rightarrow$ \texttt{TranspositionOutOfRange} (15).
|
|
\end{itemize}
|
|
\end{requirement}
|
|
|
|
\begin{requirement}
|
|
\label{req:opcat:transpose-interval-spelling}
|
|
A \texttt{TransposeInterval} \MUST{} record the spelling its interval
|
|
determined as a \texttt{SpellingAttachment} with source
|
|
\texttt{SpellingSource::Propagated}, per the core specification's Chapter~2,
|
|
\sectionsc{Spelling Sources}. The attachment's \texttt{from} is the
|
|
transposed pitch's own identifier, which the operation preserves.
|
|
|
|
That attachment alone is \emph{not sufficient}, because the default
|
|
precedence ranks \texttt{UserChosen} and \texttt{Imported} above
|
|
\texttt{Propagated} (Chapter~2, \sectionsc{Configurable Precedence}). A
|
|
\texttt{TransposeInterval} \MUST{} therefore also \textbf{move} every
|
|
engraved-layer, pitch-scoped, explicit spelling attachment on each target
|
|
that is not itself \texttt{Propagated}, preserving each attachment's
|
|
\texttt{source}, \texttt{priority}, and \texttt{layer}. A spelling is moved
|
|
by transposing its \emph{nominal} diatonically and taking whatever accidental
|
|
the transposed pitch then requires at that staff position. If any such
|
|
attachment cannot be written at the transposed position, the operation
|
|
\MUST{} refuse, atomically, with \texttt{TranspositionOutOfRange}.
|
|
\end{requirement}
|
|
|
|
\begin{rationale}
|
|
Two different failures hide here, and the propagated attachment addresses only
|
|
the first.
|
|
|
|
For a pitch with \emph{no} authored spelling, the pre-pass would re-infer, and
|
|
may pick the enharmonic the interval did not choose. The propagated attachment
|
|
pins the interval's determination.
|
|
|
|
For a pitch \emph{with} an authored spelling, the propagated attachment is
|
|
outranked and changes nothing: a C4 the author spelled ``C'', sharpened to
|
|
C-sharp~4, would still resolve to \texttt{Authored(UserChosen)} and engrave a
|
|
C natural --- the accidental simply disappears, and the notehead names a pitch
|
|
that is not there. So the authored spelling is moved instead of being left or
|
|
discarded. It is moved by its \textbf{nominal}, because the nominal is what
|
|
carries the author's enharmonic decision: an author who wrote B-sharp~3 rather
|
|
than C4 chose the letter B, and a perfect fifth up must give F-double-sharp~4,
|
|
not G. Discarding the attachment would silently lose that decision; re-inferring
|
|
it from the transposed pitch would give G.
|
|
|
|
A transposed \texttt{UserChosen} spelling is still the user's choice, so its
|
|
source is preserved. \texttt{Imported} likewise: an imported attachment asserts
|
|
``this pitch is written thus'', and once the pitch moves that assertion must
|
|
move with it or become false. Import fidelity is a property of the source file,
|
|
which a transposition does not touch.
|
|
\end{rationale}
|
|
|
|
\textbf{Conflict cases.} None --- composition is deterministic in canonical order.
|
|
|
|
\textbf{Undo semantics.} Nothing is minted. \texttt{TransposeInterval}
|
|
\MUST{} record each transposed pitch's new value into that pitch's value chain,
|
|
and that pitch's resulting \emph{engraved spelling set} into the spelling-set
|
|
chain, so that value-restoring undo recovers the pre-transpose pitch \emph{and}
|
|
its pre-transpose spelling. Restoring the pitch alone would leave a notehead
|
|
spelled for a pitch that no longer exists. This is where
|
|
\texttt{TransposeInterval} departs from the frozen \texttt{Transpose}, which
|
|
records nothing and is therefore not undoable.
|
|
|
|
\begin{requirement}
|
|
\label{req:opcat:spelling-set-chain}
|
|
The pitch's engraved spelling set is a single undo key with \emph{more than
|
|
one writer}. Every operation that changes it --- \texttt{RespellPitch} and
|
|
\texttt{TransposeInterval} alike --- \MUST{} record a write on that key.
|
|
Consequently a \texttt{RespellPitch} causally prior to a transposed
|
|
transaction is that transaction's chain-predecessor and is restored by its
|
|
undo; and a \texttt{RespellPitch} canonically \emph{later} supersedes the
|
|
transaction's write, so a \texttt{StrictInverse} undo conflicts rather than
|
|
erasing it, per the general superseded-writer rule.
|
|
|
|
A pitch's value and its engraved spelling set \MUST{} undo as one unit: if
|
|
either is superseded, neither is restored. A \texttt{BestEffort} undo
|
|
\MUSTNOT{} restore a pitch's earlier value while leaving a spelling authored
|
|
against the value it has since taken.
|
|
|
|
This unit is keyed on the \emph{pitch}, and on which keys the
|
|
\emph{transaction} wrote --- not on which operation wrote them, and not on
|
|
\texttt{TransposeInterval} in particular. A transaction whose members write
|
|
the two halves separately is coupled the same way: an editor's ``move note''
|
|
is a \texttt{ModifyIdentifiedPitch} (the value) together with a
|
|
\texttt{RespellPitch} (the spelling set), and a later respell makes a
|
|
\texttt{BestEffort} undo skip both. That breadth is intended. A transaction
|
|
that wrote only one of the two keys is unaffected, since an unwritten key
|
|
yields no supersession.
|
|
\end{requirement}
|
|
|
|
\begin{rationale}
|
|
Ratified as P13-S3. An implementation may keep the spelling-set chain
|
|
physically separate from whatever chain drives \texttt{RespellPitch}'s
|
|
last-writer-wins conflict detection --- the reference implementation does,
|
|
because folding transposes into that chain would make a concurrent respell
|
|
\emph{conflict} with a transpose and would move the canonical bytes of every
|
|
existing history. What it may not do is let one writer own the key. A chain
|
|
with a single writer cannot see the other's prior value (so undo erases it) and
|
|
cannot see the other's later value (so undo overwrites it).
|
|
\end{rationale}
|
|
|
|
An inverse interval usually exists ($(-d, -c)$), because the reduction never
|
|
saturates --- but not always: $\texttt{i32::MIN}$ has no negation. Undo does not
|
|
rely on it.
|
|
|
|
\textbf{Re-anchoring.} As \texttt{Transpose}.
|
|
|
|
\section{CreateCrossCutting}
|
|
\label{sec:k0:create-cross-cutting}
|
|
|
|
\textbf{Payload schema.} \texttt{CreateCrossCuttingOp \{ structure:
|
|
CrossCuttingValue \}}, where \texttt{CrossCuttingValue} is the typed value of a
|
|
\texttt{Tie}, \texttt{Slur}, \texttt{Beam}, or \texttt{Spanner}. Its identity and
|
|
referenced endpoints are read from the value.
|
|
|
|
\textbf{Canonical encoding.} A discriminant for the structure kind, then the
|
|
length-framed canonical bytes of the structure value.
|
|
|
|
\textbf{Reduction rule.} Set-union creation: the structure is minted live if its
|
|
id is not already live and every referenced endpoint is live; a second create of
|
|
a live id is idempotent. Graph-aware reduction materialises the structure with
|
|
its full fields.
|
|
|
|
\textbf{Conflict cases.} None (all-or-nothing creation; union is deterministic).
|
|
|
|
\textbf{Undo semantics.} Undo tombstones the minted structure, under the active
|
|
policy.
|
|
|
|
\textbf{Re-anchoring.} The structure participates in the re-anchoring rule table
|
|
when one of its endpoints is later tombstoned (see DeleteEvent).
|
|
|
|
\textbf{Authoring advisory (ratified Pass~12, closing P12-K12).} The
|
|
slur-spanning advisory reads
|
|
\texttt{Region.permits\_spanning\_slurs} \emph{conjunctively}: a slur (or
|
|
other spanner) whose endpoints lie in different regions passes the advisory
|
|
only when \emph{both} endpoint regions permit spanning. The check is
|
|
authoring-time only and never alters reduction.
|
|
|
|
\textbf{Migration coverage.} The v0$\rightarrow$v1 migration
|
|
(Chapter~\ref{ch:migration}) reconstructs the event-anchored \texttt{Tie},
|
|
\texttt{Slur}, and \texttt{Beam} from the v0 reference (id plus event endpoints).
|
|
A \texttt{Spanner} is anchored by \texttt{TimeAnchor}s rather than a fixed pair
|
|
of event endpoints, so its full value is not reconstructable from the v0
|
|
event-reference projection; a \texttt{Spanner}-create is therefore reported
|
|
unmigratable (read-only), alongside the respell case of P12-K1, and remains so.
|
|
A faithful spanner migration awaits a richer v0 projection that carries the
|
|
anchors --- a Phase-3 / Pass-12 extension, not yet implemented.
|
|
|
|
\section{DeleteCrossCutting}
|
|
\label{sec:k0:delete-cross-cutting}
|
|
|
|
\textbf{Payload schema.} \texttt{DeleteCrossCuttingOp \{ structure: TypedObjectId
|
|
\}} --- the structure named by the same key the set-union creation and the
|
|
re-anchoring table use; it \MUST{} be a cross-cutting kind
|
|
(\texttt{Tie}/\texttt{Slur}/\texttt{Beam}/\texttt{Spanner}).
|
|
|
|
\textbf{Canonical encoding.} The canonical bytes of \texttt{structure}.
|
|
|
|
\textbf{Reduction rule.} Delete-wins: the structure is tombstoned (its identifier
|
|
retained). A second delete of the same structure is idempotent; deleting a
|
|
missing or non-cross-cutting id is a no-op precondition failure. Graph-aware
|
|
reduction removes the structure from the score.
|
|
|
|
\textbf{Conflict cases.} None (delete-wins is idempotent).
|
|
|
|
\textbf{Undo semantics.} A delete mints nothing, so the prototype's minted-object
|
|
undo (Section~\ref{sec:k0:undo}) does not re-introduce the tombstoned structure
|
|
(P11-C8).
|
|
|
|
\textbf{Re-anchoring.} The deletion is direct (the structure is the target, not a
|
|
referenced endpoint); it does not itself trigger the endpoint re-anchoring table.
|
|
|
|
\section{ModifyCrossCutting}
|
|
\label{sec:k0:modify-cross-cutting}
|
|
|
|
\textbf{Payload schema.} \texttt{ModifyCrossCuttingOp \{ structure:
|
|
CrossCuttingValue \}} --- the full replacement value (v1). The replacement keeps
|
|
the structure's identity but may change its endpoints and per-kind fields; the
|
|
LWW key is the structure's \texttt{CrossCuttingValue::id}.
|
|
|
|
\textbf{Canonical encoding.} The discriminant and length-framed bytes of the
|
|
\texttt{CrossCuttingValue} (as for CreateCrossCutting).
|
|
|
|
\textbf{Reduction rule.} A last-writer-wins field overwrite keyed by structure
|
|
id. Precondition: the structure is live. The reduction re-derives the structure's
|
|
endpoints from the new value, so a later re-anchoring sees them. A malformed
|
|
replacement is a precondition no-op --- in particular a beam whose membership
|
|
falls below the two-member minimum is refused rather than materialised.
|
|
Graph-aware reduction overwrites the structure in place.
|
|
|
|
\textbf{Conflict cases.} Two concurrent differing modifications of one structure
|
|
produce a \texttt{StructuralFieldCollision} on the field \texttt{cross\_cutting}.
|
|
|
|
\textbf{Undo semantics.} Value-restoring (Section~\ref{sec:k0:undo}): undo
|
|
restores the structure's chain-predecessor value unless superseded.
|
|
|
|
\textbf{Re-anchoring.} If the target structure is tombstoned, the modification is
|
|
a no-op; re-deriving the endpoints lets a subsequent endpoint tombstone re-anchor
|
|
the structure through the standard table (see DeleteEvent).
|
|
|
|
\section{ChangeRegionTimeModel}
|
|
\label{sec:k0:change-region-time-model}
|
|
|
|
\textbf{Payload schema.} \texttt{ChangeRegionTimeModelOp \{ region: RegionId,
|
|
new\_time\_model: RegionTimeModel, declared\_incompatible: Vec<EventId>,
|
|
remapping: PositionRemapping \}} --- the full target model value (v1).
|
|
|
|
\textbf{Canonical encoding.} \texttt{region}, the length-framed
|
|
\texttt{new\_time\_model} value, the canonically-ordered
|
|
\texttt{declared\_incompatible} set, then \texttt{remapping}.
|
|
|
|
\textbf{Reduction rule.} Structural migration. The region adopts the target time
|
|
model. Graph-aware reduction derives coordinate-kind incompatibilities from the
|
|
region's events (and from the remapping coverage) and refuses a migration that
|
|
would violate the coordinate discipline.
|
|
|
|
\textbf{Conflict cases.} Concurrent same-region migrations produce a
|
|
\texttt{StructuralFieldCollision} on the field \texttt{time\_model}; a migration
|
|
with incompatible events produces a \texttt{TimeModelMigrationFailure} naming the
|
|
region and the incompatible events. A causally-later migration is re-evaluated
|
|
against the first migration's graph rather than conflicting.
|
|
|
|
\textbf{Undo semantics.} Undo restores the region's prior time model under the
|
|
active policy.
|
|
|
|
\textbf{Re-anchoring.} Not applicable.
|
|
|
|
\begin{openquestion}
|
|
\textbf{P11-C6.} The rich migration payload --- a coordinate converter rather than a
|
|
\texttt{declared\_incompatible} list plus a \texttt{PositionRemapping} --- remains
|
|
the catalog's to design when graph-aware migration is the only reduction path.
|
|
\end{openquestion}
|
|
|
|
\section{Structural Containers}
|
|
\label{sec:k0:structural-containers}
|
|
|
|
\textbf{Payload schema.} Three create/delete pairs over the region hierarchy:
|
|
\texttt{CreateRegionOp \{ region: Region \}} /
|
|
\texttt{DeleteRegionOp \{ region: RegionId \}};
|
|
\texttt{CreateStaffInstanceOp \{ region: RegionId, instance: StaffInstance \}} /
|
|
\texttt{DeleteStaffInstanceOp \{ staff\_instance: StaffInstanceId \}};
|
|
\texttt{CreateVoiceOp \{ staff\_instance: StaffInstanceId, voice: Voice \}} /
|
|
\texttt{DeleteVoiceOp \{ voice: VoiceId \}}. Each create carries the full
|
|
container value (v1); the reduction preconditions it bears \emph{no typed child
|
|
object} --- an empty container.
|
|
|
|
\textbf{Canonical encoding.} Create: the parent id (where the schema names one),
|
|
then the length-framed canonical bytes of the container value. Delete: the
|
|
container id.
|
|
|
|
\textbf{Reduction rule.} Set-union creation of an \emph{empty} container, and an
|
|
\emph{empty-only} delete-wins tombstone. A create mints the container live if its
|
|
id is fresh and (for staff instance and voice) its parent is live; it
|
|
preconditions the carried value to bear no typed child object (a region: no staff
|
|
instances, barline-alignment groups, or graphic objects; a staff instance: no
|
|
voices or measures; a voice: no events), since those carry distinct
|
|
\texttt{TypedObjectId}s the reducer mints separately --- so contents are added by
|
|
subsequent operations. A delete is a delete-wins tombstone, but a
|
|
\emph{precondition no-op} (\texttt{ContainerNotEmpty}) unless the container has no
|
|
live children --- the caller deletes contents first. Graph-aware reduction adds
|
|
or removes the container and maintains the region's staff extent so
|
|
\texttt{RegionExtents} stays satisfied.
|
|
|
|
\textbf{Conflict cases.} None at reduction time: creation is set-union (a repeat
|
|
create is idempotent), and the empty-only delete is a deterministic precondition
|
|
gate, not a conflict.
|
|
|
|
\textbf{Undo semantics.} Undo of a \emph{create} tombstones the minted container
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if it was
|
|
concurrently mutated, with the policy treatment as for InsertEvent. A
|
|
\emph{delete} mints nothing, so the prototype's minted-object undo does not
|
|
re-introduce it (P11-C8).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (the containers are minted/tombstoned by id;
|
|
the empty-only precondition means a delete never strands live children).
|
|
|
|
\section{CreateStaff}
|
|
\label{sec:k0:create-staff}
|
|
|
|
\textbf{Payload schema.} \texttt{CreateStaffOp \{ staff: Staff \}} --- the full
|
|
global-staff value (v1): identity, name, abbreviation, instrument reference,
|
|
default staff-line configuration, and optional group membership.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{staff}.
|
|
|
|
\textbf{Reduction rule.} Set-union creation of a global \texttt{Staff} on the
|
|
score root, completing the structural-container family
|
|
(Section~\ref{sec:k0:structural-containers}) upward: staff \emph{instances}
|
|
reference global staves, and until this primitive existed a resolvable staff
|
|
could only be base-seeded. A create mints the staff live if its id is fresh; a
|
|
repeat create carrying a byte-identical value reduces idempotently, and a
|
|
create whose id is already live with a \emph{differing} value is a
|
|
precondition no-op with the appended reason
|
|
\texttt{RecreateContentMismatch} (discriminant 13; ratified Pass~12, closing
|
|
P12-K9 --- the former \texttt{TargetMissing} reuse misnamed the situation:
|
|
the target is not missing, its content disagrees). The same reason applies
|
|
to the carried \texttt{TimeSignature} (Section~\ref{sec:k0:meter-tempo}),
|
|
the other re-create site at which the reducer retains the carried value for
|
|
comparison. The structural-container creates
|
|
(Section~\ref{sec:k0:structural-containers}) are plain set-union --- any
|
|
repeat create of a live id reads \texttt{AlreadyApplied} without value
|
|
comparison, since the carried value is preconditioned empty of children. Graph-aware reduction additionally preconditions that the
|
|
referenced instrument is live and, when \texttt{group} is present, that the
|
|
staff group resolves --- the mint must leave the graph satisfying the
|
|
reference-resolution invariants.
|
|
|
|
With staves mintable, \texttt{CreateStaffInstance}
|
|
(Section~\ref{sec:k0:structural-containers}) preconditions that the
|
|
instance's referenced \texttt{Staff} is live (previously the reference was
|
|
satisfiable only from the seeded base, so the check was vacuous).
|
|
|
|
\textbf{Stale-form semantics (genesis tranche G3a, disposition B, filed as
|
|
P13-S16).} \texttt{Staff.group} is the \textbf{sole authority} for group
|
|
membership; \texttt{StaffGroup.members}
|
|
(Section~\ref{sec:k0:create-staff-group}) is a non-authoritative denormalized
|
|
projection no consumer may read to decide membership. \texttt{CreateStaff}
|
|
is the operation that authors the \textbf{missing-member} stale form: a
|
|
\texttt{CreateStaffGroup(g, members: [])} followed by a
|
|
\texttt{CreateStaff(s, group: Some(g))} leaves \texttt{s.group == Some(g)}
|
|
while \texttt{g.members} omits \texttt{s} --- a \textbf{permitted} outcome,
|
|
not a defect, since G3a mints only and no operation maintains
|
|
\texttt{StaffGroup.members} by reduction. The mirror-image \textbf{spurious}
|
|
form (a staff named in \texttt{members} whose own \texttt{group} is absent or
|
|
different) is authored by \texttt{CreateStaffGroup} instead; see
|
|
Section~\ref{sec:k0:create-staff-group}'s own stale-form paragraph.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the differing-value
|
|
re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted staff
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if a live staff
|
|
instance references it (tombstoning it would strand the instance).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (a staff mint references no tombstonable
|
|
anchor; there is no \texttt{DeleteStaff} in this catalogue revision --- an
|
|
empty-only staff delete mirroring the container discipline is a later
|
|
schema-fill).
|
|
|
|
\section{CreateInstrument}
|
|
\label{sec:k0:create-instrument}
|
|
|
|
Ratified with the genesis operation tranche's first rung (G1;
|
|
\texttt{spec/CONTRACT\_GENESIS\_G1\_INSTRUMENT.md}, executing
|
|
\texttt{spec/RULING\_GENESIS\_PERSISTENCE.md}). \texttt{CreateStaff}
|
|
(Section~\ref{sec:k0:create-staff}) already requires a live
|
|
\texttt{Instrument}, and until this primitive existed nothing could mint one
|
|
except a canonical base --- so a document created empty
|
|
(\texttt{Score::empty}) and given only operations could reach a
|
|
\texttt{CreateStaff} but never satisfy it. \texttt{CreateInstrument} is the
|
|
single missing link between an empty score and a note: with it, the chain
|
|
\texttt{CreateInstrument} $\rightarrow$ \texttt{CreateStaff} $\rightarrow$
|
|
\texttt{CreateRegion} $\rightarrow$ \texttt{CreateStaffInstance}
|
|
$\rightarrow$ \texttt{CreateVoice} $\rightarrow$ \texttt{InsertEvent}
|
|
materializes a note-bearing score from operations alone.
|
|
|
|
\textbf{Payload schema.} \texttt{CreateInstrumentOp \{ instrument: Instrument
|
|
\}} --- the full abstract-instrument value (schema major 2): identity, name,
|
|
declared pitch range, abbreviation, sound configuration, transposition
|
|
interval, default clef, default staff-line configuration, and unpitched
|
|
members. Unlike every carried type in
|
|
Section~\ref{sec:k0:structural-containers} and
|
|
Section~\ref{sec:k0:create-staff}, \texttt{Instrument} holds no outbound
|
|
entity reference, so this primitive needs no referential precondition at
|
|
all --- only mint and idempotence.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{instrument}. \texttt{Instrument}'s schema-major-2 appends
|
|
(\texttt{sound\_config}, \texttt{default\_clef},
|
|
\texttt{default\_staff\_lines}, and the rest) are mandatory fields, not
|
|
\texttt{Option}-hidden, so no lower-major layout for this payload exists:
|
|
under minimal stamping (Binary Format companion \sectionsc{Schema Major 2})
|
|
a block carrying a \texttt{CreateInstrument} is \emph{born at v2}, exactly
|
|
as \texttt{CreateStaff} is --- not the value-dependent shape
|
|
\texttt{CreateRegion}/\texttt{SetStaffLayout} use.
|
|
|
|
\textbf{Reduction rule.} Set-union creation of an \texttt{Instrument} on the
|
|
score root, mirroring \texttt{CreateStaff}'s discipline exactly since
|
|
neither carried type references another entity: a create mints the
|
|
instrument live if its id is fresh; a repeat create carrying a
|
|
byte-identical value reduces idempotently
|
|
(\texttt{NoOpReason::AlreadyApplied}); a create whose id is already live
|
|
with a \emph{differing} value is a precondition no-op with
|
|
\texttt{RecreateContentMismatch}; and a create naming a tombstoned id is a
|
|
precondition no-op with \texttt{TargetTombstoned}. There is no
|
|
graph-aware reference-resolution block, because there is nothing for one to
|
|
check.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the
|
|
differing-value re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted instrument;
|
|
\texttt{StrictInverse} conflicts if a live staff references it (tombstoning
|
|
it would strand the staff).
|
|
|
|
\textbf{Re-anchoring.} Not applicable, for the same reason as
|
|
\texttt{CreateStaff}: an instrument mint references no tombstonable anchor.
|
|
There is no \texttt{DeleteInstrument} in this catalogue revision ---
|
|
\texttt{CreateStaff} ships today with no \texttt{DeleteStaff} either, and
|
|
full delete/modify coverage of the genesis-authored fields is later
|
|
tranche work (\texttt{spec/PLAN\_GENESIS\_OPS.md} G3).
|
|
|
|
\section{SetCanvasLayoutDefaults}
|
|
\label{sec:k0:set-canvas-layout-defaults}
|
|
|
|
Ratified with the genesis operation tranche's G2a rung (\texttt{spec/CONTRACT\_
|
|
GENESIS\_G2A\_SETTINGS.md}, executing \texttt{spec/RULING\_GENESIS\_
|
|
PERSISTENCE.md}): the canvas's default layout advisories
|
|
(\texttt{Canvas.layout\_defaults}) join the operation-authored surfaces, on
|
|
exactly the \texttt{SetMetadata} pattern (Section~\ref{sec:k0:score-settings}).
|
|
|
|
\textbf{Payload schema.} \texttt{SetCanvasLayoutDefaultsOp \{ layout\_defaults:
|
|
CanvasLayoutDefaults \}} --- the full page-size-and-margins value.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{layout\_defaults}. \texttt{CanvasLayoutDefaults} carries no
|
|
schema-major-1-or-higher appends of its own --- the versioning that labels the
|
|
type ``schema major 1'' lives in the \emph{containing} \texttt{Canvas} walk,
|
|
not in this leaf --- so, as a standalone payload, it has exactly one layout
|
|
and this operation is schema major~0 unconditionally.
|
|
|
|
\textbf{Reduction rule.} An advisory last-writer-wins field overwrite,
|
|
identical in discipline to \texttt{SetMetadata}: the latest write in canonical
|
|
order silently wins and the operation always applies --- no working state, no
|
|
conflict, and no idempotence short-circuit (a re-write of an identical value
|
|
is a legitimate new write, not a no-op). Graph-aware reduction overwrites
|
|
\texttt{Canvas.layout\_defaults}.
|
|
|
|
\textbf{Conflict cases.} None (advisory LWW).
|
|
|
|
\textbf{Undo semantics.} A value-restoring field overwrite
|
|
(Section~\ref{sec:k0:undo}): the write chain is seeded from the base value at
|
|
ingest, so undoing the first operational write restores the pre-operational
|
|
state rather than absence.
|
|
|
|
\textbf{Re-anchoring.} Not applicable: the field is a score singleton with no
|
|
tombstonable anchor.
|
|
|
|
\section{SetSpellingPrecedence}
|
|
\label{sec:k0:set-spelling-precedence}
|
|
|
|
Ratified with the genesis operation tranche's G2a rung, alongside
|
|
\texttt{SetCanvasLayoutDefaults} above: the score's spelling-precedence
|
|
configuration (\texttt{Score.spelling\_precedence}) joins the
|
|
operation-authored surfaces, on the same \texttt{SetMetadata} pattern.
|
|
|
|
\textbf{Payload schema.} \texttt{SetSpellingPrecedenceOp \{ precedence:
|
|
SpellingPrecedence \}} --- the full total-ordering value over the five
|
|
spelling-source kinds.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{precedence}. \texttt{SpellingPrecedence} has never been versioned ---
|
|
every codec walk that reads it, frozen and live alike, reads it through the
|
|
plain unversioned codec --- so this operation is schema major~0
|
|
unconditionally, exactly as \texttt{SetCanvasLayoutDefaults} is.
|
|
|
|
\textbf{Reduction rule.} An advisory last-writer-wins field overwrite,
|
|
identical in discipline to \texttt{SetMetadata} and
|
|
\texttt{SetCanvasLayoutDefaults}: no working state, no conflict, no
|
|
idempotence short-circuit. Graph-aware reduction overwrites
|
|
\texttt{Score.spelling\_precedence}.
|
|
|
|
\textbf{Conflict cases.} None (advisory LWW).
|
|
|
|
\textbf{Undo semantics.} A value-restoring field overwrite
|
|
(Section~\ref{sec:k0:undo}): the write chain is seeded from the base value at
|
|
ingest, so undoing the first operational write restores the pre-operational
|
|
state rather than absence.
|
|
|
|
\textbf{Re-anchoring.} Not applicable: the field is a score singleton with no
|
|
tombstonable anchor.
|
|
|
|
\section{SetTuningContext}
|
|
\label{sec:k0:set-tuning-context}
|
|
|
|
Ratified with the genesis operation tranche's G2b rung (\texttt{spec/
|
|
CONTRACT\_GENESIS\_G2B\_TUNING.md}, executing \texttt{spec/
|
|
RULING\_GENESIS\_PERSISTENCE.md}): the score's tuning context settings
|
|
(\texttt{Score.tuning\_context}) join the operation-authored surfaces, on
|
|
the same \texttt{SetMetadata} pattern as \texttt{SetCanvasLayoutDefaults}
|
|
and \texttt{SetSpellingPrecedence} above. Unlike either sibling,
|
|
\texttt{SetTuningContext} is the \textbf{sole genesis payload born at
|
|
schema major~3} and is the surface that raises the
|
|
\texttt{OperationEnvelopeBlock} accept-set from \texttt{[0, 2]} to
|
|
\texttt{[0, 3]} (Binary Format companion \sectionsc{The Chunk-Level Gate}).
|
|
|
|
\textbf{Payload schema.} \texttt{SetTuningContextOp \{ settings:
|
|
TuningContextSettings \}}. \texttt{TuningContextSettings} is the
|
|
\textbf{authored subset} of \texttt{ScoreTuningContext}: exactly its five
|
|
wire-bearing fields (\texttt{default\_pitch\_space},
|
|
\texttt{default\_tuning\_system}, \texttt{reference}, \texttt{smufl},
|
|
\texttt{overrides}), in the codec's existing order --- not the full
|
|
six-field graph type. \texttt{accidental\_extensions} is deliberately
|
|
\textbf{not} a field of the carried payload: it is staged out of schema
|
|
major~3 (Binary Format companion \sectionsc{Schema Major 3}) and
|
|
\texttt{OperationSet::accept} stores the authored envelope as a
|
|
\emph{value}, so a payload carrying the full \texttt{ScoreTuningContext}
|
|
would let \texttt{accidental\_extensions} reach the graph on the authoring
|
|
replica while decoding to empty on every replica that received the
|
|
document through serialization --- the same document in two graph states,
|
|
depending only on whether it was just authored or just reloaded. The
|
|
subset type makes this divergence unrepresentable: a field the payload
|
|
does not carry cannot be set inconsistently.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{settings}. \texttt{TuningContextSettings}'s encoding is
|
|
\textbf{byte-identical} to \texttt{ScoreTuningContext}'s existing
|
|
five-field wire walk --- a type-level narrowing, not a new wire form. Since
|
|
\texttt{ScoreTuningContext}'s \texttt{smufl} and \texttt{overrides} appends
|
|
are mandatory (not \texttt{Option}-hidden), no lower-major layout for this
|
|
payload exists at any value: \texttt{SetTuningContext} is \textbf{born at
|
|
schema major~3 unconditionally}, the same shape as \texttt{CreateInstrument}'s
|
|
unconditional birth at major~2 (Section~\ref{sec:k0:create-instrument}), not
|
|
the value-dependent shape \texttt{CreateRegion}/\texttt{SetStaffLayout} use.
|
|
|
|
\textbf{Reduction rule.} An advisory last-writer-wins field overwrite,
|
|
identical in discipline to \texttt{SetMetadata}: no working state, no
|
|
conflict, and no idempotence short-circuit. Graph-aware reduction writes
|
|
exactly the five subset fields onto \texttt{Score.tuning\_context} and
|
|
\textbf{leaves \texttt{accidental\_extensions} untouched} --- preserving
|
|
whatever the graph already held, since the operation carries no
|
|
information about that field and normalizing it away (clearing it at
|
|
construction, or rejecting a populated in-memory value) would either make
|
|
correctness depend on a clearing discipline the compiler cannot see, or
|
|
turn an in-memory-only field into an authoring error for callers who never
|
|
opted into its persistence.
|
|
|
|
\textbf{Conflict cases.} None (advisory LWW).
|
|
|
|
\textbf{Undo semantics.} A value-restoring field overwrite
|
|
(Section~\ref{sec:k0:undo}), with the chain's value type the \textbf{subset}
|
|
\texttt{TuningContextSettings}, not \texttt{ScoreTuningContext}: the write
|
|
chain is seeded from the base's five wire-bearing fields at ingest, so
|
|
undoing the first operational write restores the pre-operational settings
|
|
rather than absence, identically whether the base was seeded with the type
|
|
default or a genuine non-default value --- the never-authored /
|
|
authored-to-default distinction is unobservable and stays so, since
|
|
\texttt{ScoreTuningContext} is an always-valued \texttt{Score} field, like
|
|
\texttt{metadata}, with no absent state to return to. \texttt{accidental\_
|
|
extensions} never participates in undo: no operation ever writes it, so
|
|
undo leaves it exactly as it found it.
|
|
|
|
\textbf{Re-anchoring.} Not applicable: the field is a score singleton with no
|
|
tombstonable anchor.
|
|
|
|
\section{CreateStaffGroup}
|
|
\label{sec:k0:create-staff-group}
|
|
|
|
Ratified with the genesis operation tranche's G3a rung (\texttt{spec/
|
|
CONTRACT\_GENESIS\_G3A\_ENTITIES.md}, executing \texttt{spec/RULING\_
|
|
GENESIS\_PERSISTENCE.md}): a global staff grouping (\texttt{Score.
|
|
staff\_groups}) joins the operation-authored surfaces, on the
|
|
\texttt{CreateStaff} set-union mint pattern (Section~\ref{sec:k0:create-staff}).
|
|
|
|
\textbf{Payload schema.} \texttt{CreateStaffGroupOp \{ group: StaffGroup \}}
|
|
--- the full staff-group value: identity, optional name, kind (grand staff,
|
|
bracket, sub-bracket, choral, or a registered extension kind), and the
|
|
carried \texttt{members} list.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{group}. \texttt{StaffGroup} has never been versioned, so this
|
|
operation is schema major~0 unconditionally.
|
|
|
|
\textbf{Reduction rule.} Set-union creation, mirroring \texttt{CreateStaff}
|
|
exactly: a create mints the group live if its id is fresh; a repeat create
|
|
carrying a byte-identical value reduces idempotently
|
|
(\texttt{NoOpReason::AlreadyApplied}); a create whose id is already live with
|
|
a differing value is a precondition no-op with \texttt{RecreateContentMismatch};
|
|
a create naming a tombstoned id is a precondition no-op with
|
|
\texttt{TargetTombstoned}. Graph-aware reduction additionally preconditions
|
|
that every id in \texttt{members} resolves to a live \texttt{Staff} ---
|
|
reusing \texttt{TargetMissing} (discriminant 0), no new
|
|
\texttt{PreconditionFailureReason} --- and is skipped entirely base-free,
|
|
which has no staff universe to check against.
|
|
|
|
\textbf{Stale-form semantics (disposition B, filed as P13-S16).}
|
|
\texttt{Staff.group} (Section~\ref{sec:k0:create-staff}) is the \textbf{sole
|
|
authority} for group membership; \texttt{members} here is a
|
|
\textbf{non-authoritative denormalized projection}, stored exactly as carried
|
|
and \textbf{neither maintained nor trusted} by any subsequent reduction. This
|
|
operation authors the \textbf{spurious-member} stale form: a
|
|
\texttt{CreateStaff(s, group: None)} followed by
|
|
\texttt{CreateStaffGroup(g, members: [s])} leaves \texttt{g.members == [s]}
|
|
while \texttt{s.group} stays \texttt{None} (or names a different group) ---
|
|
a \textbf{permitted} outcome, not a defect. No consumer may read
|
|
\texttt{members} to decide whether a staff belongs to a group.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the differing-value
|
|
re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted group
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if a live staff
|
|
still names it (a stale reference, not a strand --- \texttt{Staff.group}
|
|
degrading to point at a tombstoned id is the same shape core Chapter~6's
|
|
re-anchoring rule table already treats for other dangling references).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (a staff-group mint references no
|
|
tombstonable anchor; there is no \texttt{DeleteStaffGroup} in this catalogue
|
|
revision, mirroring \texttt{CreateStaff}'s own absent delete).
|
|
|
|
\section{CreatePartDefinition}
|
|
\label{sec:k0:create-part-definition}
|
|
|
|
Ratified with genesis tranche G3a, alongside \texttt{CreateStaffGroup} above:
|
|
a part-extraction view definition (\texttt{Score.parts}) joins the
|
|
operation-authored surfaces, on the same \texttt{CreateStaff} set-union mint
|
|
pattern.
|
|
|
|
\textbf{Payload schema.} \texttt{CreatePartDefinitionOp \{ part:
|
|
PartDefinition \}} --- identity, name, and the carried \texttt{staves} list.
|
|
Parts are projections, not storage (core specification Chapter~5
|
|
\sectionsc{Parts}): only references and overrides.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{part}. \texttt{PartDefinition} has never been versioned, so this
|
|
operation is schema major~0 unconditionally.
|
|
|
|
\textbf{Reduction rule.} Set-union creation, mirroring \texttt{CreateStaff}
|
|
exactly (mint/idempotence/mismatch/tombstone as above). Graph-aware reduction
|
|
additionally preconditions that every id in \texttt{staves} resolves to a
|
|
live \texttt{Staff} (\texttt{TargetMissing}, no new failure reason), skipped
|
|
base-free.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the differing-value
|
|
re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted part
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if concurrently
|
|
mutated.
|
|
|
|
\textbf{Re-anchoring.} Not applicable (a part mint references no tombstonable
|
|
anchor; there is no \texttt{DeletePartDefinition} in this catalogue
|
|
revision).
|
|
|
|
\section{CreateAnalysisLayer}
|
|
\label{sec:k0:create-analysis-layer}
|
|
|
|
Ratified with genesis tranche G3a: a first-class analysis layer
|
|
(\texttt{Score.analysis\_layers}) joins the operation-authored surfaces, on
|
|
the \texttt{CreateInstrument} mint pattern
|
|
(Section~\ref{sec:k0:create-instrument}) --- not \texttt{CreateStaff}'s,
|
|
since \texttt{AnalysisLayer} holds no outbound entity reference.
|
|
|
|
\textbf{Payload schema.} \texttt{CreateAnalysisLayerOp \{ layer:
|
|
AnalysisLayer \}} --- identity and name only (core specification Chapter~5
|
|
\sectionsc{Analysis Layers and Views}).
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{layer}. \texttt{AnalysisLayer} has never been versioned, so this
|
|
operation is schema major~0 unconditionally.
|
|
|
|
\textbf{Reduction rule.} Set-union creation, mirroring \texttt{CreateInstrument}
|
|
exactly: mint/idempotence/mismatch/tombstone as above, with \textbf{no}
|
|
graph-aware reference-resolution block --- there is nothing for one to check.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the differing-value
|
|
re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted layer
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if a live view
|
|
still names it in \texttt{active\_layers} (Section~\ref{sec:k0:create-view}).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (an analysis-layer mint references no
|
|
tombstonable anchor; there is no \texttt{DeleteAnalysisLayer} in this
|
|
catalogue revision).
|
|
|
|
\section{CreateView}
|
|
\label{sec:k0:create-view}
|
|
|
|
Ratified with genesis tranche G3a, the fourth and final root-level mint of
|
|
this rung: a view recipe (\texttt{Score.views}) joins the operation-authored
|
|
surfaces, on the \texttt{CreateStaff} set-union mint pattern.
|
|
|
|
\textbf{Payload schema.} \texttt{CreateViewOp \{ view: ViewDefinition \}} ---
|
|
identity, name, and the carried \texttt{active\_layers} list. The
|
|
view-\emph{kind} detail (core specification Chapter~7) is out of scope for
|
|
this baseline.
|
|
|
|
\textbf{Canonical encoding.} The length-framed canonical bytes of
|
|
\texttt{view}. \texttt{ViewDefinition} has never been versioned, so this
|
|
operation is schema major~0 unconditionally.
|
|
|
|
\textbf{Reduction rule.} Set-union creation, mirroring \texttt{CreateStaff}
|
|
exactly (mint/idempotence/mismatch/tombstone as above). Graph-aware reduction
|
|
additionally preconditions that every id in \texttt{active\_layers} resolves
|
|
to a live \texttt{AnalysisLayer} (\texttt{TargetMissing}, no new failure
|
|
reason), skipped base-free. \textbf{This packet is self-contained}: a
|
|
\texttt{CreateView}'s precondition target can be minted by a
|
|
\texttt{CreateAnalysisLayer} earlier in the same operation set, so the
|
|
ordering is testable end-to-end without a canonical base.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union; the differing-value
|
|
re-create is a precondition gate, not a conflict).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted view
|
|
(Section~\ref{sec:k0:undo}); \texttt{StrictInverse} conflicts if concurrently
|
|
mutated.
|
|
|
|
\textbf{Re-anchoring.} Not applicable (a view mint references no tombstonable
|
|
anchor; there is no \texttt{DeleteView} in this catalogue revision).
|
|
|
|
\section{Repeat Structures}
|
|
\label{sec:k0:repeat-structures}
|
|
|
|
Ratified with the schema-major-2 revision: the dedicated authoring pair for
|
|
\texttt{RepeatStructure} (core specification Chapter~5
|
|
\sectionsc{Repeat Structures}). Repeats live in the cross-cutting registry
|
|
but are \emph{not} \texttt{CrossCuttingValue} kinds on the wire --- the
|
|
cross-cutting operations admit only
|
|
\texttt{Tie}/\texttt{Slur}/\texttt{Beam}/\texttt{Spanner}
|
|
(Section~\ref{sec:k0:create-cross-cutting}) --- so the pair is a
|
|
first-class primitive mirroring the cross-cutting disciplines.
|
|
|
|
\textbf{Payload schema.} \texttt{CreateRepeatStructureOp \{ repeat:
|
|
RepeatStructure \}} --- the full value (schema major 2): identity, the
|
|
\texttt{start}/\texttt{end} anchors, the \texttt{RepeatKind}, and the
|
|
volta list. \texttt{DeleteRepeatStructureOp \{ repeat: RepeatStructureId
|
|
\}}.
|
|
|
|
\textbf{Canonical encoding.} Create: the length-framed canonical bytes of
|
|
\texttt{repeat} (the schema-major-2 layout). Delete: the bare identifier.
|
|
The create's payload embeds the v2 \texttt{RepeatStructure} layout
|
|
unconditionally (\texttt{kind} and \texttt{voltas} are not optional
|
|
fields), so under minimal stamping a block carrying one stamps schema
|
|
major~2 --- the create is \emph{born at v2}. The delete's payload is an
|
|
identifier --- a major-0 layout --- so minimal stamping gives its blocks
|
|
major~0; the kind discriminant itself is an append-only schema-\emph{minor}
|
|
vocabulary event (the same append mechanism as the Phase-3 tranche; the
|
|
stamp itself always follows minimal stamping over the payload, per the
|
|
Binary Format companion \sectionsc{Schema Major 2}).
|
|
|
|
\textbf{Reduction rule.} Set-union creation and a delete-wins tombstone,
|
|
mirroring the cross-cutting family. A create mints the repeat live if its
|
|
id is fresh and \emph{every} event-referencing anchor site resolves to a
|
|
live event --- \texttt{start}/\texttt{end}, the kind's jump targets
|
|
(\texttt{DaCapo.end\_target},
|
|
\texttt{DalSegno.segno}/\texttt{end\_target}), and each volta's
|
|
\texttt{start}/\texttt{end} --- since the mint must leave the graph
|
|
satisfying the reference-resolution invariants; a dead anchor is a
|
|
precondition no-op (\texttt{TargetMissing}). A repeat create of a live id
|
|
reads \texttt{AlreadyApplied} without value comparison (the cross-cutting
|
|
discipline; the \texttt{RecreateContentMismatch} scope of
|
|
Section~\ref{sec:k0:create-staff} is unchanged). A delete tombstones the
|
|
repeat (idempotent on re-delete, delete-wins); deleting a missing id is a
|
|
no-op precondition failure. Graph-aware reduction adds or removes the
|
|
structure from the cross-cutting registry.
|
|
|
|
\textbf{Conflict cases.} None at reduction time (set-union creation;
|
|
delete-wins is idempotent).
|
|
|
|
\textbf{Undo semantics.} Undo of a create tombstones the minted repeat
|
|
(Section~\ref{sec:k0:undo}). A delete mints nothing, so the prototype's
|
|
minted-object undo does not re-introduce the tombstoned structure
|
|
(P11-C8).
|
|
|
|
\textbf{Re-anchoring.} The structure participates in the re-anchoring rule
|
|
table when a referenced event is later tombstoned: re-anchor to the
|
|
nearest surviving anchor, cascade-delete only when none survives --- as
|
|
for spanners, spanning \emph{every} anchor site
|
|
(\texttt{start}/\texttt{end}, jump targets, volta spans). Among multiple
|
|
surviving candidates --- a case slurs and spanners never present, since
|
|
their sole other endpoint is the forced survivor --- ``nearest'' is
|
|
currently realized as the deterministic identifier-order minimum (the
|
|
same tie-break the two-endpoint collapse already used); proximity-aware
|
|
(four-key) selection over a repeat's surviving sites is a deferred
|
|
refinement, exactly as the spanner row defers per-kind proximity bounds.
|
|
The rule row is the core specification's (Chapter~6 re-anchoring rule
|
|
table, ratified with this pair).
|
|
|
|
\textbf{Authoring advisory.} The volta well-formedness constraints of
|
|
core Chapter~5 (endings non-empty, 1-based, \emph{strictly} ascending) are
|
|
\emph{advisory}: surfaced at authoring time under interactive validation,
|
|
never enforced under reduction.
|
|
|
|
\section{SetUserSystemBreak}
|
|
\label{sec:k0:set-user-system-break}
|
|
|
|
\textbf{Payload schema.} \texttt{SetUserSystemBreakOp \{ region: RegionId,
|
|
anchor: TimeAnchor, present: bool \}} --- the full anchor value (v1).
|
|
|
|
\textbf{Canonical encoding.} \texttt{region}, the length-framed \texttt{anchor}
|
|
value, then the boolean.
|
|
|
|
\textbf{Reduction rule.} A last-writer-wins advisory. The break preference is
|
|
recorded for the region keyed by the anchor's \emph{resolved musical position};
|
|
graph-aware reduction adds or removes the anchor from the region's user
|
|
system-break list.
|
|
|
|
\textbf{Conflict cases.} None (LWW advisory).
|
|
|
|
\textbf{Undo semantics.} Undo restores the prior advisory value for the
|
|
\texttt{(region, resolved-position)} key.
|
|
|
|
\textbf{Re-anchoring.} Not applicable in the prototype (the advisory is keyed by
|
|
resolved position; a tombstoned anchor target degrades to the region origin).
|
|
|
|
\section{Score Settings}
|
|
\label{sec:k0:score-settings}
|
|
|
|
\textbf{Payload schema.} Three score-level field overwrites:
|
|
\texttt{SetMetadataOp \{ metadata: ScoreMetadata \}} overwrites the score
|
|
singleton; \texttt{SetMetricGridOp \{ region: RegionId, grid: Option<MetricGrid>
|
|
\}} overwrites (or clears) a region's default metric grid;
|
|
\texttt{SetUserPageBreakOp \{ region: RegionId, anchor: TimeAnchor, present: bool
|
|
\}} is the page-break sibling of SetUserSystemBreak.
|
|
|
|
\textbf{Canonical encoding.} Metadata: the length-framed \texttt{metadata} value.
|
|
Metric grid: \texttt{region}, then an \texttt{Option} discriminant and (when
|
|
present) the length-framed \texttt{grid} value. Page break: \texttt{region}, the
|
|
length-framed \texttt{anchor} value, then the boolean.
|
|
|
|
\textbf{Reduction rule.} Three field overwrites differing only in discipline.
|
|
\emph{SetMetadata} is an \textbf{advisory} last-writer-wins: the latest write in
|
|
canonical order silently wins and the operation always applies --- no working
|
|
state and no conflict (the same discipline as SetUserSystemBreak, on the score
|
|
singleton). \emph{SetMetricGrid} is a \textbf{structural} field overwrite keyed
|
|
by region: precondition the region is live and staff-based (a FreeGraphic region
|
|
has no metric-grid slot --- the op is a no-op there), and reject a grid whose
|
|
meter sequence names a time signature that is not live (the Chapter~5 invariant
|
|
forbids installing such a grid). \emph{SetUserPageBreak} is a canonical LWW
|
|
advisory keyed by the anchor's resolved musical position, with the same
|
|
staff-based precondition. Graph-aware reduction overwrites the metadata singleton,
|
|
sets the region's default metric grid, or adds/removes the page-break anchor
|
|
under its resolved-position key (so two anchors resolving to one position occupy a
|
|
single slot).
|
|
|
|
\textbf{Conflict cases.} SetMetadata and SetUserPageBreak: none (advisory LWW).
|
|
SetMetricGrid: two concurrent differing grids for one region produce a
|
|
\texttt{StructuralFieldCollision} on the field \texttt{metric\_grid}.
|
|
|
|
\textbf{Undo semantics.} All three are value-restoring field overwrites
|
|
(Section~\ref{sec:k0:undo}): undo restores the prior metadata, grid, or break
|
|
preference from the key's write chain unless superseded.
|
|
|
|
\textbf{Re-anchoring.} The advisory breaks degrade as for SetUserSystemBreak; the
|
|
metric grid and metadata are keyed by region / singleton and do not re-anchor (a
|
|
deleted region's settings are no-ops --- \texttt{TargetMissing}).
|
|
|
|
\section{Meter and Tempo Overwrites}
|
|
\label{sec:k0:meter-tempo}
|
|
|
|
\textbf{Payload schema.} The finer-grained metric-model overwrites beneath the
|
|
whole-grid \texttt{SetMetricGrid} (Section~\ref{sec:k0:score-settings}):
|
|
\texttt{SetTimeSignatureOp \{ region: RegionId, anchor: TimeAnchor,
|
|
time\_signature: Option<TimeSignature> \}} sets, replaces, or (\texttt{None})
|
|
removes the single \texttt{MeterChange} at the anchor's resolved musical
|
|
position in the region's default metric grid, carrying the full
|
|
\texttt{TimeSignature} value (v1); \texttt{SetTempoSegmentOp \{ region:
|
|
Option<RegionId>, start: TimeAnchor, segment: Option<TempoSegment> \}} sets,
|
|
replaces, or removes the single tempo segment starting at the resolved
|
|
position, in the score-level tempo map (\texttt{region: None}) or the region's
|
|
local map (\texttt{Some}; a set on a region with no local map creates one).
|
|
|
|
\textbf{Canonical encoding.} Time signature: \texttt{region}, the
|
|
length-framed \texttt{anchor}, then an \texttt{Option} discriminant and (when
|
|
present) the length-framed \texttt{time\_signature} value. Tempo segment: an
|
|
\texttt{Option} discriminant and (when present) \texttt{region}, then the
|
|
length-framed \texttt{start}, then an \texttt{Option} discriminant and (when
|
|
present) the length-framed \texttt{segment}.
|
|
|
|
\textbf{Reduction rule.} Last-writer-wins structural overwrites keyed by
|
|
\texttt{(region, resolved position)} (time signature) and \texttt{(scope,
|
|
resolved start)} (tempo segment). A carried \texttt{TimeSignature} is minted
|
|
set-union under the same discipline as \texttt{CreateStaff}: fresh id mints;
|
|
byte-identical re-carry is idempotent; a differing value under a live id is a
|
|
precondition no-op (\texttt{RecreateContentMismatch},
|
|
Section~\ref{sec:k0:create-staff}). The time-signature value's beat-group sum is validated at
|
|
construction and again at decode, so a malformed value never reaches
|
|
reduction. A tempo-segment write preconditions that the \emph{resulting} map
|
|
is well-formed (segments ordered and non-overlapping; a non-constant shape
|
|
carries its end data; the carried segment's own start equals the operation's
|
|
\texttt{start} key) --- a write that would malform the map is refused as a
|
|
precondition no-op (\texttt{TempoMapMalformed}). Graph-aware reduction applies
|
|
the meter change to \texttt{default\_metric\_grid.meter\_sequence} and the
|
|
segment to the scoped tempo map.
|
|
|
|
\textbf{Conflict cases.} Two concurrent differing writes of one key produce a
|
|
\texttt{StructuralFieldCollision} on the field \texttt{meter\_sequence} or
|
|
\texttt{tempo\_segments} respectively, with the standard winner/loser
|
|
recording; identical concurrent writes reduce idempotently.
|
|
|
|
\textbf{Undo semantics.} Value-restoring per Section~\ref{sec:k0:undo}: undo
|
|
restores the key's chain-predecessor value (or its absence).
|
|
|
|
\textbf{Re-anchoring.} An event-anchored \texttt{anchor}/\texttt{start} whose
|
|
event is later tombstoned degrades by the framework's anchor rules; the
|
|
overwrite keys on the \emph{resolved} position, so the recorded change
|
|
survives its anchor.
|
|
|
|
\begin{openquestion}
|
|
\textbf{P12-C5.} A mid-region meter change authored by
|
|
\texttt{SetTimeSignature} reduces cleanly and materialises into the grid, but
|
|
the notational-decomposition pre-pass currently honours only a region's
|
|
\emph{first} governing meter (P12-H4's single-meter simplification), so the
|
|
derived notation ignores the change until multi-meter decomposition lands.
|
|
The reduction-level semantics are pinned here; the derived-annotation gap is
|
|
P12-H4's.
|
|
\end{openquestion}
|
|
|
|
\section{SetStaffLayout}
|
|
\label{sec:k0:set-staff-layout}
|
|
|
|
\textbf{Payload schema.} \texttt{SetStaffLayoutOp \{ staff\_instance:
|
|
StaffInstanceId, instrument\_override: Option<InstrumentId>,
|
|
staff\_lines\_override: Option<StaffLineConfiguration>, visible: bool \}} ---
|
|
the non-break layout advisories with a graph home: the staff instance's three
|
|
inline advisory fields, overwritten as a unit.
|
|
|
|
\textbf{Canonical encoding.} \texttt{staff\_instance}, an \texttt{Option}
|
|
discriminant and (when present) \texttt{instrument\_override}, an
|
|
\texttt{Option} discriminant and (when present) the length-framed
|
|
\texttt{staff\_lines\_override}, then the boolean.
|
|
|
|
\textbf{Reduction rule.} A last-writer-wins \emph{advisory} overwrite keyed by
|
|
\texttt{staff\_instance}. Preconditions: the staff instance is live; a present
|
|
\texttt{instrument\_override} resolves to a live instrument under graph-aware
|
|
reduction. The richer engraving-override vocabulary (stem direction, notehead
|
|
shape, custom positions) has no durable graph home yet and remains projected
|
|
layout state --- extending this primitive to cover it is staged with the
|
|
data-model expansion.
|
|
|
|
\textbf{Conflict cases.} None (LWW advisory).
|
|
|
|
\textbf{Undo semantics.} Value-restoring per Section~\ref{sec:k0:undo}.
|
|
|
|
\textbf{Re-anchoring.} If the staff instance is tombstoned, the overwrite is a
|
|
no-op (\texttt{TargetTombstoned}).
|
|
|
|
\section{DeclareTransaction}
|
|
\label{sec:k0:declare-transaction}
|
|
|
|
\textbf{Payload schema.} \texttt{TransactionDescriptor \{ id: TransactionId,
|
|
label: String, category: Option<TransactionCategory> \}}. Value-complete in v0
|
|
and unchanged.
|
|
|
|
\textbf{Reduction rule.} Records the descriptor. Member primitives reference the
|
|
transaction id and \MUST{} causally depend on the descriptor; the members reduce
|
|
atomically (all-or-nothing) in canonical order.
|
|
|
|
\textbf{Conflict cases.} A missing descriptor or a member that does not causally
|
|
follow it produces a \texttt{TransactionConflict}; any member failure rolls back
|
|
the whole transaction and all members read \texttt{NoOp\{TransactionConflict\}}.
|
|
|
|
\textbf{Undo / re-anchoring.} Transactions are the unit of undo
|
|
(Section~\ref{sec:k0:undo}); re-anchoring is per member.
|
|
|
|
\section{ResolveConflict (meta-operation)}
|
|
\label{sec:k0:resolve-conflict}
|
|
|
|
\textbf{Payload schema.} \texttt{ResolveConflictPayload \{ target: ConflictId,
|
|
action: ResolutionAction \}}. Value-complete.
|
|
|
|
\textbf{Reduction rule.} Transitions the target conflict's resolution state. An
|
|
action of \texttt{Dismiss} reaches the \texttt{Dismissed} state; any other action
|
|
reaches \texttt{Resolved}. Re-resolving with the same action is idempotent; two
|
|
concurrent resolves with differing actions produce a meta-conflict.
|
|
|
|
\emph{Beyond the concurrent case (ratified Pass~12, closing P12-K4).} The
|
|
earliest-applied-resolve-governs rule is \emph{universal}: a causally-later
|
|
resolve with a differing action does not supersede the first --- it reduces
|
|
\texttt{AlreadyApplied} --- and \emph{any} resolve targeting a
|
|
\texttt{Dismissed} conflict likewise reads \texttt{AlreadyApplied}.
|
|
Intentional re-resolution is deliberately outside the v1 operation set; a
|
|
future dedicated operation (a \texttt{ReopenConflict}-class primitive) is the
|
|
sanctioned path if it is ever needed. The meta-conflict record names both
|
|
resolver operation ids in \texttt{caused\_by}; conflict records themselves
|
|
have no \texttt{TypedObjectId} kind (deliberate --- they are materialized
|
|
state, not graph objects), so the contested conflict is identified by the
|
|
\texttt{equivocation}-style field key, not an object reference.
|
|
|
|
\begin{rationale}
|
|
Pass~11 added \texttt{ResolutionAction::Dismiss} (item 2.5) precisely so the
|
|
\texttt{Dismissed} state is reachable by an authored operation rather than merely
|
|
representable. The catalog records that \texttt{Dismiss} is the action that
|
|
selects it (resolving the v0 ambiguity P11-C10).
|
|
\end{rationale}
|
|
|
|
\section{ResolveEquivocation (meta-operation)}
|
|
\label{sec:k0:resolve-equivocation}
|
|
|
|
\textbf{Payload schema.} \texttt{ResolveEquivocationPayload \{ target:
|
|
OperationId, chosen: EnvelopeHash \}} --- the equivocated slot and the
|
|
candidate envelope (by canonical-bytes hash) that shall stand. Value-complete.
|
|
|
|
\textbf{Canonical encoding.} \texttt{target} (16 canonical bytes), then
|
|
\texttt{chosen} (32 bytes), per the codec baseline.
|
|
|
|
\textbf{Reduction rule.} Order-independent promotion of an equivocated slot
|
|
(core specification Chapter~6, \sectionsc{Equivocation}): when the operation
|
|
set holds an \texttt{Equivocated} slot for \texttt{target} and \texttt{chosen}
|
|
names one of its candidates, the slot reduces as if it had always been
|
|
\texttt{Single} with the chosen envelope --- the chosen candidate contributes
|
|
to canonical reduction at its own canonical position, and operations that were
|
|
pending on the equivocated id unblock. Among multiple resolves naming the same
|
|
slot, the one earliest in canonical order governs; a later resolve naming the
|
|
\emph{same} candidate reduces idempotently (\texttt{AlreadyApplied}). The
|
|
resolve operation must itself occupy a \texttt{Single} slot; an equivocated
|
|
resolve is excluded from reduction like any other equivocated slot. A resolved
|
|
slot records no \texttt{OperationSlotEquivocated} anomaly; the losing
|
|
candidates remain in the diagnostic candidate store only.
|
|
|
|
\textbf{Conflict cases.} Two resolves of one slot naming \emph{differing}
|
|
candidates produce a \texttt{StructuralFieldCollision} meta-conflict on the
|
|
field \texttt{equivocation\_resolution}, recording the governing resolve
|
|
(earlier in canonical order) as winner and the later as loser, with both
|
|
operations in \texttt{caused\_by} --- the same discipline as
|
|
\texttt{ResolveConflict} meta-conflicts. Preconditions: a resolve whose
|
|
\texttt{target} is not an equivocated slot, or whose \texttt{chosen} is not
|
|
among the slot's candidates, is a precondition no-op.
|
|
|
|
\textbf{Edge semantics (ratified Pass~12, closing P12-K6).} Promotion is
|
|
\emph{single-pass}, not fixpoint: a promoted candidate that is itself a
|
|
\texttt{ResolveEquivocation} does not govern a further promotion in the same
|
|
reduction. A resolve in a \emph{quarantined} replica segment is excluded from
|
|
reduction and never governs. A resolve held \emph{pending} by its own causal
|
|
gaps still governs promotion --- the verdict is a pure function of the slot
|
|
map (set-level), while the resolve's own effect stays pending. The
|
|
invalid-target/invalid-chosen no-op reuses \texttt{TargetMissing}; a dedicated
|
|
reason was considered and rejected (the appended-reason budget is spent where
|
|
a distinct verdict changes caller behavior, which it does not here).
|
|
|
|
\textbf{Undo semantics.} Mints nothing; not inverted under the prototype's
|
|
minted-object undo (P11-C8).
|
|
|
|
\textbf{Re-anchoring.} Not applicable (the payload references an operation
|
|
slot, not a graph object).
|
|
|
|
\begin{rationale}
|
|
The core specification names three resolution paths for an equivocated slot:
|
|
transport-level reconciliation, this explicit operation, and a
|
|
profile-declared deterministic selection policy. This entry pins the schema
|
|
for the explicit-operation path, which the core specification previously named
|
|
only in prose. The profile-policy path is \emph{deferred with a named landing
|
|
site} (Pass~12 disposition of P12-K5): v1 profiles declare \emph{no}
|
|
selection function, and the hook's definition belongs to the Profile
|
|
Conformance companion when it is written --- the reducer deliberately carries
|
|
no policy hook until a profile can declare one.
|
|
\end{rationale}
|
|
|
|
\section{UndoTransaction (meta-operation)}
|
|
\label{sec:k0:undo}
|
|
|
|
\textbf{Payload schema.} \texttt{UndoTransactionPayload \{ target: TransactionId,
|
|
policy: UndoPolicy \}}, with \texttt{UndoPolicy} one of \texttt{StrictInverse},
|
|
\texttt{BestEffort}, \texttt{Cascade}. Value-complete.
|
|
|
|
\textbf{Reduction rule.} A forward compensating edit computed against the
|
|
materialised state at the undo's canonical position (never literal time
|
|
travel). The compensation has two parts.
|
|
|
|
\emph{Minted-object tombstoning} (as before): every object the target
|
|
transaction minted is tombstoned. \texttt{StrictInverse} conflicts
|
|
(\texttt{TombstonedTarget}) if any minted object was already tombstoned;
|
|
\texttt{BestEffort} tombstones the survivors.
|
|
|
|
\emph{Value restoration} (this revision): for every last-writer-wins overwrite
|
|
the target transaction performed --- event and identified-pitch modification,
|
|
respelling, cross-cutting modification, metadata, canvas layout defaults,
|
|
spelling precedence, tuning context settings, metric grid, meter change,
|
|
tempo segment, staff layout, and the user break advisories --- the reducer
|
|
maintains, per overwritten key, the \emph{canonical-order write chain} of
|
|
(writer, value) pairs. Undoing the transaction restores each written key to
|
|
its chain-predecessor value (or its absence, where the transaction introduced
|
|
the first value), \emph{provided the transaction's write is still the key's
|
|
last writer}. Because the chain is keyed by canonical order, the restored
|
|
value is a pure function of the operation set: permutation-invariant by
|
|
construction. When a causally-later or canonically-later write has
|
|
superseded the key, \texttt{StrictInverse} refuses the whole undo with a
|
|
\texttt{TransactionConflict} conflict naming the undo and the superseding
|
|
writer; \texttt{BestEffort} restores the still-last-written keys and skips
|
|
the superseded ones. Restorations are expressed in the effect status (a fully
|
|
clean compensation is \texttt{Applied}; a mixed one is
|
|
\texttt{AppliedWithRepair} carrying only the tombstone repairs) --- no new
|
|
repair vocabulary.
|
|
|
|
\emph{Strand-blocks (ratified Pass~12, closing P12-K10).} A
|
|
\texttt{StrictInverse} undo that refuses to tombstone a minted object still
|
|
referenced by a live non-member (e.g., a staff whose instance survives outside
|
|
the transaction) records \texttt{ConflictKind::TransactionConflict} --- the
|
|
reuse is blessed: the strand-block \emph{is} a transaction-scoped conflict of
|
|
the undo, and the conflict record's affected objects and description carry the
|
|
strand detail. No dedicated undo conflict kind is added.
|
|
|
|
\emph{Idempotence asymmetry (ratified Pass~12, closing P12-K11).} An undo's
|
|
value restorations enter the write chains as ordinary writes by the undo
|
|
operation --- no distinguished undo provenance. Consequently a \emph{second}
|
|
undo of the same transaction finds each restored key superseded by the first
|
|
undo and refuses (\texttt{Conflicted} under \texttt{StrictInverse}, skipped
|
|
under \texttt{BestEffort}), while \emph{absence} restorations (not
|
|
representable as chain writes) repeat idempotently. The asymmetry is
|
|
normative, documented behavior; a chain-native undo provenance would be
|
|
revisited only under the deferred undo-as-operation (streaming-consistent
|
|
undo) design, which subsumes this question.
|
|
|
|
\emph{Still deferred} (P11-C8, narrowed): re-introducing content tombstoned by
|
|
\emph{delete} primitives (a deterministic resurrection needs a system-derived
|
|
identifier derivation the ratified closed tag set does not yet include); and
|
|
\texttt{Cascade}'s dependent-closure computation --- \texttt{Cascade} remains
|
|
\texttt{StrictInverse} over the same set. \texttt{Transpose} inversion is no
|
|
longer listed: \texttt{TransposeInterval} is undone by value restoration, not by
|
|
applying an inverse interval (Push~4a, closing P12-K2). An inverse
|
|
$(-d, -c)$ does exist wherever both components are negatable --- the reduction
|
|
never saturates --- but $\texttt{i32::MIN}$ has none, so the inverse is partial
|
|
and undo does not depend on it. The frozen \texttt{Transpose} is undone by
|
|
neither.
|
|
|
|
% ===========================================================================
|
|
\chapter{v0 \texorpdfstring{$\rightarrow$}{->} v1 Payload Migration}
|
|
\label{ch:migration}
|
|
|
|
A v0 envelope carries an identifier-only payload; a v1 envelope carries the
|
|
value-typed payload this catalog defines. The two forms do not coexist as
|
|
permanent dialects (that would double the reducer surface forever); instead the
|
|
catalog ships a \textbf{one-time migration} that lifts a v0 envelope to v1 using
|
|
the score graph as context, applied once on read. Production code carries only v1
|
|
payloads; v0 envelopes survive only as a regression corpus.
|
|
|
|
\begin{requirement}
|
|
\label{req:migration:properties}
|
|
The migration \texttt{migrate\_v0\_envelope(v0, context: \&Score)} \MUST{} be
|
|
\textbf{deterministic} (two implementations migrating the same v0 envelope
|
|
against the same context produce byte-identical v1 envelopes) and
|
|
\textbf{equivalence-preserving} (a v0 envelope and its v1 migration reduce to
|
|
byte-identical canonical \texttt{MaterializedState}). When a value cannot be
|
|
reconstructed from the v0 projection plus the context, the migration \MUST{}
|
|
report the envelope unmigratable rather than fabricate a value, and the bundle
|
|
opens read-only.
|
|
\end{requirement}
|
|
|
|
The reference implementation (\texttt{epiphany-ops::migrate}) reconstructs the
|
|
\texttt{InsertEvent} event, the \texttt{DeleteEvent} compensation, the
|
|
\texttt{ChangeRegionTimeModel} model, the \texttt{SetUserSystemBreak} anchor, and
|
|
the event-anchored cross-cutting structures (\texttt{Tie} / \texttt{Slur} /
|
|
\texttt{Beam}) self-containedly from the v0 projection; a \texttt{Spanner},
|
|
anchored by \texttt{TimeAnchor}s rather than event endpoints, remains unmigratable
|
|
until the projection carries them. It recovers a \texttt{RespellPitch} spelling
|
|
from the context (P12-K1,
|
|
Section~\ref{sec:k0:respell-pitch}). The migration's merge gate
|
|
(\texttt{epiphany-testkit::migration}) drives the inverse direction --- projecting
|
|
a v1 corpus to v0 and migrating it back --- and asserts byte-identical reduction
|
|
plus a non-vacuity guard.
|
|
|
|
% ===========================================================================
|
|
\chapter{K1 --- Framework Slots (Phase 3)}
|
|
\label{ch:k1}
|
|
|
|
This chapter drafted the remaining catalogue items as framework slots. The
|
|
Phase-2 \textbf{M2} expansion (the broad-K0 groups in \texttt{epiphany-ops})
|
|
implemented four groups of them; with the \textbf{M2e} catalogue expansion their
|
|
full per-primitive schemas now appear in Chapter~\ref{ch:k0}, so they are
|
|
\emph{normative under the Phase-2 profile} and an implementation \MUST{}
|
|
\emph{not} reject them. They are cross-referenced first. The genuinely Phase-3
|
|
slots that remain \textbf{unavailable} are listed second: an implementation
|
|
\MUST{} reject an operation of one of \emph{those} kinds. Each remaining slot is a
|
|
schema-fill against the template of Chapter~\ref{ch:framework}; adding one is not
|
|
a fresh design.
|
|
|
|
\section*{Implemented since M2 (now in Chapter~\ref{ch:k0})}
|
|
|
|
\begin{description}
|
|
\item[Modify event; identified-pitch operations; transpose]
|
|
M2 Group~1 --- Sections~\ref{sec:k0:modify-event},
|
|
\ref{sec:k0:identified-pitch}, and \ref{sec:k0:transpose}.
|
|
\item[Delete / modify cross-cutting]
|
|
M2 Group~2 --- Sections~\ref{sec:k0:delete-cross-cutting} and
|
|
\ref{sec:k0:modify-cross-cutting} (creation is
|
|
Section~\ref{sec:k0:create-cross-cutting}).
|
|
\item[Create / delete region / staff instance / voice]
|
|
M2 Group~3 --- Section~\ref{sec:k0:structural-containers} (set-union creation
|
|
and the empty-only delete).
|
|
\item[Set metadata / metric grid / user page break]
|
|
M2 Group~4 --- Section~\ref{sec:k0:score-settings} (advisory metadata,
|
|
structural metric grid, advisory page break).
|
|
\end{description}
|
|
|
|
\section*{Implemented in the Phase-3 first tranche (now in Chapter~\ref{ch:k0})}
|
|
|
|
\begin{description}
|
|
\item[Create staff; set time signature / tempo segment; set layout]
|
|
Sections~\ref{sec:k0:create-staff}, \ref{sec:k0:meter-tempo}, and
|
|
\ref{sec:k0:set-staff-layout}. The disciplines are as drafted: set-union
|
|
creation, LWW structural overwrite, and LWW advisory respectively.
|
|
\end{description}
|
|
|
|
\section*{Added in the schema-major-2 revision (Chapter~\ref{ch:k0})}
|
|
|
|
\begin{description}
|
|
\item[Create / delete repeat structure]
|
|
Section~\ref{sec:k0:repeat-structures} --- net-new primitives (never
|
|
drafted as framework slots). Set-union creation with the
|
|
all-anchors-live precondition and a delete-wins tombstone; the create
|
|
is born at v2, the delete stamps major~0.
|
|
\end{description}
|
|
|
|
\section*{Retired slots (ratified Pass~12: outside the operation set)}
|
|
|
|
\begin{description}
|
|
\item[Create score / canvas --- retired, closing P12-K8; narrowed by
|
|
\texttt{spec/RULING\_GENESIS\_PERSISTENCE.md}]
|
|
The document root and the canvas remain \emph{structural givens}, not
|
|
operation products: \texttt{TypedObjectId} has no Canvas kind, and the
|
|
root is never op-minted (core specification, Chapter~5
|
|
\sectionsc{The Canvas}). \texttt{RULING\_GENESIS\_PERSISTENCE} reverses
|
|
only the wider claim this slot's description used to carry --- that the
|
|
score's \emph{contents} arrive outside the operation set. They do not:
|
|
the genesis tranche makes every mutable \texttt{Score} field
|
|
operation-authored, \texttt{Canvas.layout\_defaults} among them
|
|
(Section~\ref{sec:k0:set-canvas-layout-defaults}). What survives, and is
|
|
load-bearing, is the narrower claim above --- the canvas \emph{itself} is
|
|
still never minted, addressed, or deleted by any operation, and no
|
|
\texttt{TypedObjectId} kind exists for it. These are not ``unavailable
|
|
slots'' awaiting a design --- no operation kind will be assigned to them.
|
|
The decision is revisited only if an addressable multi-canvas model is
|
|
adopted at a future schema major.
|
|
\end{description}
|
|
|
|
\begin{nongoal}
|
|
The full $60$--$80$-primitive catalogue is not a Phase-2 deliverable. The
|
|
framework (Chapter~\ref{ch:framework}) and the K0 representative set
|
|
(Chapter~\ref{ch:k0}) are sufficient to exercise every reduction discipline; the
|
|
remaining primitives are Phase-3 schema-fill.
|
|
\end{nongoal}
|
|
|
|
\end{document}
|