epiphany/spec/operation_catalog.tex

696 lines
32 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
}
\newtcolorbox{requirement}[1][]{
enhanced, breakable,
colback=white, colframe=epiphanygold,
fonttitle=\bfseries\color{white}, title={\scshape\hspace{2pt}Requirement},
coltitle=white, colbacktitle=epiphanygold,
arc=1pt, boxrule=0pt, leftrule=2pt,
left=10pt, right=10pt, top=8pt, bottom=8pt,
attach boxed title to top left={xshift=0pt, yshift=0pt},
boxed title style={arc=0pt, sharp corners, boxrule=0pt, left=6pt, right=8pt, top=2pt, bottom=2pt},
#1
}
\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.1.0 --- Phase 2 (K0 representative primitives)}\\[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 v0.1 release delivers the catalog \emph{framework} and the
\textbf{K0 representative primitive set} --- the operation kinds the Phase~2
visible slice and binary format actually exercise. The remaining K0 and 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} with the schema, reduction rule, conflict cases, undo
semantics, and re-anchoring behaviour defined there. The K1 primitives of
Chapter~\ref{ch:k1} are \emph{unavailable} under the Phase-2 profile: an
implementation \MUST{} reject (not silently ignore) an operation whose kind is a
K1 primitive it does not implement.
% ===========================================================================
\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.} 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}).
\begin{openquestion}
\textbf{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 is the one representative payload that is not self-contained
under migration; the disposition (whether a richer v0 corpus, or a documented
read-only fallback, is the long-term answer) is a Pass-12 question.
\end{openquestion}
\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{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) under M1, alongside the respell case of P12-K1. A
faithful spanner migration joins when the projection carries the anchors --- M2.
\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{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{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.
\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{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 prototype models the compensation as tombstoning the objects the target
transaction minted: \texttt{StrictInverse} conflicts (\texttt{TombstonedTarget})
if any minted object was already tombstoned; \texttt{BestEffort} tombstones the
survivors; \texttt{Cascade} is \texttt{StrictInverse} over the same set
(dependent-closure undo is a Phase-3 refinement, P11-C8).
% ===========================================================================
\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 cross-cutting structure self-containedly from the v0 projection; 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}
The following K0 catalogue bullet items are \emph{drafted} here as framework
slots and completed in Phase~3. Under the Phase-2 profile they are
\textbf{unavailable}: an implementation \MUST{} reject an operation of one of
these kinds. Each is a schema-fill against the template of
Chapter~\ref{ch:framework}; adding one is not a fresh design.
\begin{description}
\item[Create score / canvas / region / staff / staff instance / voice]
Structural mint operations. Discipline: set-union creation
(Section~\ref{sec:k0:create-cross-cutting}); undo tombstones the mint;
re-anchoring is not applicable.
\item[ModifyEvent]
Field overwrite on an event's non-identity fields (articulations, dynamics,
stem). Discipline: last-writer-wins with structural-field-collision
(Section~\ref{sec:k0:respell-pitch}).
\item[Insert / delete / modify identified pitch]
Pitch-level mint, tombstone, and field overwrite within an event. Disciplines
as for the event-level analogues.
\item[Set metadata (title / composer / lyricist / copyright)]
Field overwrite on score metadata. Discipline: LWW advisory
(Section~\ref{sec:k0:set-user-system-break}).
\item[Set metric grid / time signature / tempo segment]
Structural field overwrite on a region's metric model. Discipline:
last-writer-wins, with a structural-field-collision on concurrent differing
grids.
\item[Delete / update tie / slur / beam / spanner]
Cross-cutting tombstone and field overwrite. Disciplines: delete-wins with
re-anchoring (Section~\ref{sec:k0:delete-event}) and field overwrite.
\item[Set layout / system-break advisory]
The page/layout advisory companion to
Section~\ref{sec:k0:set-user-system-break}.
\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}