Schema major 1 Phase A: ratify the v1 wire form + migration (spec only)
The first phase of the first-ever binary-format schema-major bump (v0 -> v1),
the machinery-first minimal major. Spec-only: ratifies the contract that
Phases B-F build against; no code changes.
core_spec.tex: defines the two referenced-but-undefined types that were the
P12-I7 / P12-K7 gaps -- CanvasLayoutDefaults (with core geometry primitives
CanvasSize/CanvasMargins in staff spaces via CanonicalF64, A4/8mm default,
since core has no geometry types and must not depend on layout-ir) and
PitchRange (advisory pitch compass used by Instrument.range and
IndeterminacyHints) -- and adds Region.permits_spanning_slurs (default false).
Records schema major 1 as the first data-model expansion major and tightens
the minor-version rule (a field add, even Option, is major; minor = append
discriminants to the companion's append-safe vocabularies only).
binary_format.tex -> 0.3.0: the full "Schema Major 1" section --
- Where the changed fields reach: Canvas.layout_defaults and
Instrument.range are snapshot-only (no CreateCanvas/CreateInstrument op),
but Region.permits_spanning_slurs also reaches the CANONICAL CreateRegion
operation payload (CreateRegion embeds the full Region). The canonical-base
MaterializedState embeds none of these and stays major 0, byte-identical.
- Cross-major reader rules: discard-and-regenerate non-canonical chunks;
parse-or-read-only for canonical ones, so a major-0 reader opens a bundle
carrying v1 CreateRegion ops read-only.
- Accept-set gate [min,max] (rejects majors outside the set); per-payload-
type major assignment; the changed v1 value layouts (the wire form ratifies
the reduced reference-code layout, not the fuller data model); the total
default-filling v0->v1 migration table (including the CreateRegion payload).
- Length-prefix unification NARROWED to the resolved-layout (its own
non-canonical LayoutCache): the barrier/extension blobs stay regime (b) u64
because they ride the canonical manifest, which stays major 0.
Two review passes hardened this checkpoint. The first caught that Region is a
canonical operation payload (not cache-only, as the architecture analysis had
assumed) -- user chose to embrace it and build the canonical op-payload
migration. It also surfaced the barrier-blobs-in-manifest constraint that
narrows the unification. The second refined the minor-version delegation, the
accept-set outside-[min,max] semantics, and stale "no defined type" text in
the reference-suite / quality-metric companions and the engrave DECISIONS.
P12-I7 moved to IN PROGRESS (spec type defined here; code graph home lands in
Phase C). Both companions and the engrave DECISIONS reworded accordingly. All
four affected PDFs rebuild clean (0 undefined references).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
This commit is contained in:
parent
9d28dd6615
commit
f4a2f1fdf0
|
|
@ -111,12 +111,14 @@ inputs the solver cannot measure.
|
|||
be the spec's intent. Break constraints are evaluated against the **final
|
||||
break structure**.
|
||||
4. **Page geometry is an engraver parameter (`PageGeometry`), defaulted to A4
|
||||
at an 8 mm staff.** The spec names `Canvas.layout_defaults` ("paper size,
|
||||
margins") but never defines the type, and core does not implement it;
|
||||
adding a graph field now would violate the companion's frozen-layout rule,
|
||||
so the graph home (`CanvasLayoutDefaults`) is **staged to the data-model
|
||||
schema major** and the engraver takes the geometry as a constructor
|
||||
parameter. Default arithmetic (1 staff space = staff height / 4 = 2.0 mm at
|
||||
at an 8 mm staff.** Adding a `Canvas` graph field is a schema-major change
|
||||
under the companion's frozen-layout rule, so it was staged to the data-model
|
||||
schema major. **Schema major 1 now defines the type** (`CanvasLayoutDefaults
|
||||
{ page_size: CanvasSize, margins: CanvasMargins }`, staff spaces, A4/8mm
|
||||
default) and ratifies its wire form (core spec + Binary Format 0.3.0,
|
||||
Phase A); the **code graph home lands in Phase C** (`Canvas` gains the
|
||||
field) and the engraver reads it in Phase C′. Until then the engraver takes
|
||||
the geometry as a constructor parameter. Default arithmetic (1 staff space = staff height / 4 = 2.0 mm at
|
||||
an 8 mm staff): A4 210 × 297 mm → **105 × 148.5** staff spaces; 15 mm
|
||||
margins → **7.5** staff spaces; content area 180 × 267 mm → **90 × 133.5**
|
||||
staff spaces. 90 staff spaces wraps the ten-measure hand-off fixture
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ code instead is the failure mode this batch exists to prevent.
|
|||
| P12-K9 | `epiphany-ops` K | Differing-value re-creates (a live id re-carried with different content: `CreateStaff`, the carried `TimeSignature`, container creates) refuse with `TargetMissing`, which misnames the situation. Decide whether a dedicated `PreconditionFailureReason` (appended) is warranted. | G / Pass 12 (vocabulary) |
|
||||
| P12-K10 | `epiphany-ops` K | Undo strand-blocks (StrictInverse refusing to tombstone a minted object still referenced by a live non-member, e.g. a staff with a surviving instance) reuse `ConflictKind::TransactionConflict`. Decide whether undo refusals deserve their own conflict kind. | G / Pass 12 (undo) |
|
||||
| P12-K11 | `epiphany-ops` K | An undo's value restorations enter the write chains as ordinary writes by the undo op, so a second undo of the same transaction sees the first as a superseding writer (Conflicted/skip) while absence-restorations repeat idempotently — a documented asymmetry. Decide whether chain writes need distinguished undo provenance so repeated undo is uniformly idempotent. | G / Pass 12 (undo) |
|
||||
| P12-I7 | `epiphany-engrave` I | Page geometry has no graph home: the spec names `Canvas.layout_defaults` ("paper size, margins") but defines no type, and adding a `Canvas` field is a schema-major change under the companion's frozen-layout rule. Casting-off therefore uses an engraver-side `PageGeometry` default (A4 at an 8mm staff: 105x148.5 ss, 7.5 ss margins, documented arithmetic). Define `CanvasLayoutDefaults` with the data-model schema-major; until then solvers MAY default. | G (graph model, schema-major) |
|
||||
| P12-I7 **(IN PROGRESS — schema-major-1 track)** | `epiphany-engrave` I | Page geometry had no graph home: the spec named `Canvas.layout_defaults` ("paper size, margins") but defined no type, and adding a `Canvas` field is a schema-major change under the companion's frozen-layout rule. Casting-off therefore uses an engraver-side `PageGeometry` default (A4 at an 8mm staff: 105x148.5 ss, 7.5 ss margins). **Phase A of the schema-major-1 bump defines the type** (`CanvasLayoutDefaults { page_size: CanvasSize, margins: CanvasMargins }` in staff spaces, A4/8mm default) and ratifies its v1 wire form (Binary Format 0.3.0); the **graph home lands in code in Phase C** (`Canvas` gains the field), after which the engraver reads it (Phase C′) — until fully landed, solvers MAY default. | G (graph model, schema-major) |
|
||||
| P12-I8 | `epiphany-engrave` I | Break-constraint satisfaction predicate: implemented as "a `SystemBreakAt`/`PageBreakAt` is satisfied iff the final layout starts a system/page at that slot" (a region-first slot is trivially satisfied). Ch7/Ch9 never define satisfaction for break constraints; ratify the predicate. | G / Pass 12 (solver) |
|
||||
| P12-I9 | `epiphany-layout-ir` I | Honouring a user break must attribute the decision to its override (`DecisionSource::UserOverride(id)`), but constraints carry no override identity; implemented via a `ConstrainedLayoutIR.break_origins` sidecar populated by `to_constrained`. Bless the sidecar or widen the normalized constraint record. | G / Pass 12 (solver) |
|
||||
| P12-I10 | `epiphany-layout-ir` I | System-spanning strokes split at system boundaries need synthesized provenance for continuation segments; implemented as `SynthesisKind::Registered(SYSTEM_CONTINUATION_SYNTHESIS)` with a deterministic `(original, ordinal)` instance key. Add a first-class continuation synthesis kind or bless the registered id. | G / Pass 12 (provenance) |
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -235,7 +235,7 @@
|
|||
{\Large\scshape\color{epiphanyslate}Binary Format}\\[6pt]
|
||||
{\large\itshape\color{epiphanyslate}A companion to the Core Specification}\\[14pt]
|
||||
{\color{epiphanygold}\rule{3in}{0.8pt}}\\[24pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.2.0 --- Phase 2/3 (canonical wire format: primitives through bundle physical layout + K0 and Phase-3-tranche payload framing)}\\[4pt]
|
||||
{\normalsize\color{epiphanyink}Version 0.3.0 --- Phase 2/3 (canonical wire format: primitives through bundle physical layout + K0 and Phase-3-tranche payload framing; schema major~1 data-model expansion)}\\[4pt]
|
||||
{\small\color{epiphanyslate}Normative for the byte layouts it defines}
|
||||
\vfill
|
||||
\end{titlepage}
|
||||
|
|
@ -395,7 +395,10 @@ which.
|
|||
\texttt{ResolvedLayoutIR} canonical output
|
||||
(Chapter~\ref{ch:noncanon}). &
|
||||
Little-endian integers; \texttt{u64} little-endian counts and length
|
||||
prefixes. Deliberate, golden-locked divergence from regime (a). \\
|
||||
prefixes --- a golden-locked divergence from regime (a). Schema major~1
|
||||
moves the \emph{resolved-layout} surface to \texttt{u32}; the
|
||||
manifest-embedded barrier blobs stay \texttt{u64}
|
||||
(Section~\ref{sec:evolution:major1}). \\
|
||||
(c) & Identifiers and hash-derived forms: the 128-bit identifier family,
|
||||
\texttt{ReplicaId}, \texttt{OperationId}, \texttt{ConflictId}, the
|
||||
\texttt{TypedObjectId} discriminant, digest truncations
|
||||
|
|
@ -406,14 +409,20 @@ which.
|
|||
\end{longtable}
|
||||
|
||||
\begin{rationale}
|
||||
Regimes (a) and (b) differ only in prefix width, and the divergence is
|
||||
\emph{tolerated rather than repaired}: the two surfaces were golden-locked
|
||||
independently (the barrier blobs and the resolved-layout output carry their
|
||||
own literal-byte anchors), they never embed one another's framing, and
|
||||
unifying the prefix width now would break locked bytes for zero benefit. The
|
||||
divergence is recorded as an open question for the next schema-major revision
|
||||
(Chapter~\ref{ch:evolution}). Regime (c) is not a divergence at all: an
|
||||
identifier's byte form \emph{is} its sort key, so big-endian is load-bearing.
|
||||
In schema major~0, regimes (a) and (b) differ only in prefix width, and the
|
||||
divergence was \emph{tolerated rather than repaired}: the two surfaces were
|
||||
golden-locked independently (the barrier blobs and the resolved-layout output
|
||||
carry their own literal-byte anchors), they never embed one another's framing,
|
||||
and unifying the prefix width within major~0 would break locked bytes for zero
|
||||
benefit. Once a major bump was scheduled for data-model reasons, part of the
|
||||
unification became free to carry: schema major~1 moves the resolved-layout
|
||||
surface (an independent non-canonical \texttt{LayoutCache}) to regime (a)'s
|
||||
\texttt{u32} prefixes and re-locks its goldens. The barrier blobs cannot follow
|
||||
in the same step --- they are carried opaquely inside the \emph{canonical}
|
||||
manifest, which stays major~0 --- so they keep regime (b) until a manifest-
|
||||
revising major (Chapter~\ref{ch:evolution}). Regime (c) is not a divergence at
|
||||
all: an identifier's byte form \emph{is} its sort key, so big-endian is
|
||||
load-bearing.
|
||||
\end{rationale}
|
||||
|
||||
\section{Primitive Composition Rules}
|
||||
|
|
@ -2078,8 +2087,13 @@ conservative treatment of unknown extensions --- are core Chapter~8
|
|||
bytes only.
|
||||
|
||||
These layouts are \textbf{regime (b)}: all counts and length prefixes are
|
||||
\texttt{u64} little-endian --- a deliberate, golden-locked divergence from
|
||||
the \texttt{u32} regime (Section~\ref{sec:conventions:regimes}).
|
||||
\texttt{u64} little-endian --- a deliberate, golden-locked divergence from the
|
||||
\texttt{u32} regime (Section~\ref{sec:conventions:regimes}). They \textbf{keep
|
||||
this form in schema major~1}: unlike the resolved-layout surface, these blobs
|
||||
are carried \emph{opaquely inside the canonical manifest}
|
||||
(Section~\ref{sec:bundle:manifest}), which stays major~0, so a major-0 reader
|
||||
must continue to parse them as \texttt{u64}. Their unification waits for a
|
||||
future major that revises the manifest (Section~\ref{sec:evolution:major1}).
|
||||
|
||||
\section{Framing}
|
||||
\label{sec:barriers:framing}
|
||||
|
|
@ -2202,7 +2216,9 @@ The core specification delegates schema evolution to this document (core
|
|||
Chapter~8, \sectionsc{Schema Versioning}: \emph{``Schema evolution is
|
||||
governed by the Binary Format companion specification, which defines the
|
||||
wire encoding for each schema version''}). This chapter defines the wire
|
||||
rules for schema major~0.
|
||||
rules for schema evolution. Chapters~\ref{ch:values}--\ref{ch:barriers}
|
||||
specify the schema-major-0 layouts; Section~\ref{sec:evolution:major1}
|
||||
specifies the schema-major-1 delta and the migration between them.
|
||||
|
||||
\section{The Chunk-Level Gate}
|
||||
\label{sec:evolution:gate}
|
||||
|
|
@ -2212,7 +2228,13 @@ Every chunk declares a \texttt{SchemaVersion} (major, minor) in its
|
|||
|
||||
\begin{itemize}
|
||||
\item \textbf{Major} = incompatible. A reader \MUST{} reject a chunk whose
|
||||
schema major it does not support. The current major is~\tablenums{0}.
|
||||
schema major it does not support. A reader supports a contiguous accept-set
|
||||
$[\textsc{min}, \textsc{max}]$ of majors and rejects any chunk whose major
|
||||
falls \emph{outside} $[\textsc{min}, \textsc{max}]$ --- too new (above
|
||||
\textsc{max}) or, once \textsc{min} rises past a retired major, too old
|
||||
(Section~\ref{sec:evolution:major1}). Two majors are defined:
|
||||
\tablenums{0} and \tablenums{1}; the reference implementation's accept-set
|
||||
is $\{0, 1\}$ ($\textsc{min} = 0$, $\textsc{max} = 1$).
|
||||
\item \textbf{Minor} = additive. v0 readers verify the major only; the
|
||||
minor is a \emph{record}, not a gate --- but it is a mandatory record:
|
||||
a writer \MUST{} raise the chunk schema minor when it emits any
|
||||
|
|
@ -2232,6 +2254,15 @@ fourteenth field within major~0}. The manifest is a positional struct whose
|
|||
decoder rejects trailing bytes, so manifest-body extension is
|
||||
major-gated in v0 exactly like every other struct.
|
||||
|
||||
Adding a field is a \textsc{major} change \emph{regardless of its type}: an
|
||||
\texttt{Option} field is not ``optional'' in the wire sense --- it still
|
||||
occupies a new positional slot (a presence byte, then the payload when
|
||||
present), which shifts every subsequent field and rejects under the
|
||||
trailing-bytes rule. There is no ``downgrade to minor'' for an \texttt{Option}
|
||||
addition. The first exercise of this is schema major~1
|
||||
(Section~\ref{sec:evolution:major1}), which adds \texttt{Instrument.range}
|
||||
(an \texttt{Option}) among other fields.
|
||||
|
||||
The \emph{only} minor-additive mechanism in schema major~0 is
|
||||
\textbf{appending discriminants to open vocabularies}:
|
||||
|
||||
|
|
@ -2273,14 +2304,181 @@ Operation Catalog (\sectionsc{v0 $\rightarrow$ v1 Payload Migration})
|
|||
because it was semantic rather than byte-level
|
||||
(Section~\ref{sec:ops:v0}); byte-level migrations belong here.
|
||||
|
||||
\begin{openquestion}
|
||||
Whether the \texttt{u64}/\texttt{u32} length-prefix divergence between
|
||||
regimes (a) and (b) (Section~\ref{sec:conventions:regimes}) gets unified at
|
||||
the next schema major. Unification would simplify decoders and cost one
|
||||
coordinated re-lock of the barrier-blob and resolved-layout goldens;
|
||||
leaving it is free but permanent. Decide when the first major bump is
|
||||
scheduled for other reasons --- the divergence alone does not justify one.
|
||||
\end{openquestion}
|
||||
The \texttt{u64}/\texttt{u32} length-prefix divergence between regimes (a) and
|
||||
(b) (Section~\ref{sec:conventions:regimes}), left open in earlier revisions for
|
||||
a future major, is \textbf{partly resolved in schema major~1}
|
||||
(Section~\ref{sec:evolution:major1}): the first major bump, scheduled for
|
||||
data-model reasons, carries the unification for the independent non-canonical
|
||||
resolved-layout surface. The barrier/extension blobs remain regime~(b) because
|
||||
they ride the canonical manifest, which this bump keeps at major~0; their
|
||||
unification stays open for a manifest-revising major.
|
||||
|
||||
\section{Schema Major 1}
|
||||
\label{sec:evolution:major1}
|
||||
|
||||
Schema major~1 is the first data-model expansion major, defined here in full:
|
||||
the changed value layouts, the length-prefix unification, and the
|
||||
byte-for-byte migration from major~0. It bundles every change that was
|
||||
deferred to ``the next major'' so that one coordinated migration discharges
|
||||
them together.
|
||||
|
||||
\subsection{Where the changed fields reach}
|
||||
The three added fields land in different chunk classes, and the major is
|
||||
assigned \textbf{per payload type}, not per chunk kind:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{Canvas.layout\_defaults} and \texttt{Instrument.range} appear
|
||||
\emph{only} in the acceleration-cache full-\texttt{Score} snapshot (a
|
||||
\texttt{ChunkKind::Snapshot}). No operation payload embeds a
|
||||
\texttt{Canvas} or an \texttt{Instrument} value (there is no
|
||||
\texttt{CreateCanvas}/\texttt{CreateInstrument}; canvas is the inline
|
||||
genesis singleton and instruments live in score genesis), so these two
|
||||
are confined to that one \textbf{non-canonical} chunk.
|
||||
\item \texttt{Region.permits\_spanning\_slurs} reaches the snapshot
|
||||
\emph{and} the \textbf{canonical operation layer}:
|
||||
\texttt{CreateRegion} embeds the full \texttt{Region} value
|
||||
(Section~\ref{sec:ops:payload}), so its v1 payload carries the v1
|
||||
\texttt{Region} layout. An operation-envelope block containing a
|
||||
\texttt{CreateRegion} therefore encodes v1 bytes and is stamped
|
||||
\textbf{major~1}; a block with no such operation stays \textbf{major~0}.
|
||||
Operation-envelope blocks are canonical, so this is a canonical
|
||||
operation-layer change, not merely a cache change.
|
||||
\item \texttt{ChunkKind::Snapshot} is itself payload-polymorphic: the same
|
||||
kind carries \emph{either} an acceleration-cache full \texttt{Score} (as
|
||||
above, stamped major~1) \emph{or} the canonical-base
|
||||
\texttt{MaterializedState}, disambiguated by manifest role. The canonical
|
||||
base embeds no \texttt{Canvas}, \texttt{Instrument}, or \texttt{Region}
|
||||
value, so it is unchanged and stays \textbf{major~0}. Its content-hash
|
||||
preimage (Section~\ref{sec:bundle:chunks}) includes the schema version, so
|
||||
re-stamping the unchanged base at major~1 would change its chunk id and
|
||||
churn every \texttt{SnapshotRef} for zero layout benefit. A writer
|
||||
\MUSTNOT{} do so; a conformance test \SHOULD{} assert the canonical base
|
||||
is byte-identical across the bump.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Cross-major reader behaviour}
|
||||
Two rules follow from the chunk classes above and the core specification's
|
||||
\sectionsc{Schema Versioning} canonical/non-canonical distinction:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Non-canonical chunks --- discard and regenerate.} The layout
|
||||
caches and the acceleration snapshot are non-canonical. A reader meeting a
|
||||
foreign-major one \MAY{} discard and regenerate it rather than decode it: a
|
||||
major-1 reader regenerates a major-0 layout cache instead of carrying a
|
||||
frozen old-width decoder, and it migrates a major-0 acceleration
|
||||
\texttt{Score} snapshot on read (cheaper than replaying the operation log).
|
||||
\item \textbf{Canonical chunks --- parse or open read-only.} The manifest and
|
||||
the canonical base stay major~0 and are always parseable. A major-1 op
|
||||
block is canonical: a major-0-only reader \MUSTNOT{} discard it (that would
|
||||
fork canonical state) --- it opens the bundle in read-only preservation
|
||||
mode, having read the major-0 base and manifest but being unable to replay
|
||||
the v1 \texttt{CreateRegion} operations. A major-1 reader migrates such a
|
||||
block on read (below).
|
||||
\end{itemize}
|
||||
|
||||
So a major-0-only reader opens a major-1 bundle \emph{fully} only when the
|
||||
bundle contains no v1 \texttt{CreateRegion} operation; otherwise it opens
|
||||
read-only. It always reads the canonical base and manifest, and always
|
||||
discards the higher-major non-canonical caches.
|
||||
|
||||
\subsection{The accept-set gate}
|
||||
A reader supports a contiguous set of majors $[\textsc{min}, \textsc{max}]$
|
||||
(the reference implementation: $\{0, 1\}$, i.e. $\textsc{min} = 0$,
|
||||
$\textsc{max} = 1$). The chunk-level gate
|
||||
(Section~\ref{sec:evolution:gate}) rejects a chunk whose major falls outside
|
||||
$[\textsc{min}, \textsc{max}]$ --- too new above \textsc{max}, or too old
|
||||
below \textsc{min} once a reader drops support for a retired major; it no
|
||||
longer rejects on inequality with a single supported major.
|
||||
|
||||
\subsection{Changed and new value layouts}
|
||||
These extend Chapter~\ref{ch:values}'s regime~(a) rules. As there, \textbf{the
|
||||
wire form is the reference implementation's struct layout} --- which is a
|
||||
reduced subset of the core specification's fuller data model (the code's
|
||||
\texttt{Instrument} carries a subset of the model's fields, etc.). The wire
|
||||
form ratifies the code, not the model; a struct's v1 layout \textbf{appends}
|
||||
its new field(s) after its existing major-0 fields:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{Canvas} $=$ \texttt{regions} \cat{} \texttt{layout\_defaults},
|
||||
where \texttt{CanvasLayoutDefaults} $=$ \texttt{page\_size}
|
||||
(\texttt{CanvasSize} $=$ \texttt{width} \cat{} \texttt{height}, two
|
||||
\texttt{CanonicalF64} leaves) \cat{} \texttt{margins}
|
||||
(\texttt{CanvasMargins} $=$ four \texttt{CanonicalF64} leaves: top,
|
||||
right, bottom, left).
|
||||
\item \texttt{Instrument} $=$ \texttt{id} \cat{} \texttt{name} \cat{}
|
||||
\texttt{range} (\texttt{Option<PitchRange>}: a presence byte, then for
|
||||
\texttt{Some} a \texttt{PitchRange} $=$ \texttt{lowest} \cat{}
|
||||
\texttt{highest}, each a \texttt{Pitch} under this chapter's rules).
|
||||
\item \texttt{Region} appends \texttt{permits\_spanning\_slurs}, one bare
|
||||
bool byte (\tablenums{0}/\tablenums{1}), after its major-0 fields.
|
||||
\item \textbf{Operation layer:} the \texttt{CreateRegion} payload embeds a
|
||||
length-prefixed \texttt{Region} (Section~\ref{sec:ops:payload}), so its v1
|
||||
form carries the v1 \texttt{Region} layout (the appended bool inside the
|
||||
embedded value). This is the one \emph{canonical} byte change in major~1;
|
||||
the operation envelope's own field order is unchanged, only its embedded
|
||||
\texttt{Region} grows.
|
||||
\end{itemize}
|
||||
Every embedded leaf keeps this chapter's framing (a \texttt{CanonicalF64} is a
|
||||
\texttt{u32} LE length \cat{} 8 bytes $= 12$; a bool is one bare byte).
|
||||
|
||||
\subsection{Length-prefix unification (partial)}
|
||||
Regime~(b) (Section~\ref{sec:conventions:regimes}) is \emph{narrowed}, not
|
||||
fully retired, in major~1. The \texttt{ResolvedLayoutIR} output --- an
|
||||
independent, non-canonical \texttt{LayoutCache} chunk --- moves from
|
||||
\texttt{u64} to \texttt{u32} LE counts and length prefixes, matching
|
||||
regime~(a), and its golden anchors re-lock in the same step. Because it is
|
||||
non-canonical, a major-1 reader regenerates a major-0 resolved-layout cache
|
||||
rather than decoding its \texttt{u64} form --- no frozen \texttt{u64} decoder
|
||||
is required.
|
||||
|
||||
The extension-declaration and edit-barrier blobs (Chapter~\ref{ch:barriers})
|
||||
\textbf{stay regime~(b)}: they are carried opaquely inside the canonical
|
||||
manifest (Section~\ref{sec:bundle:manifest}), which stays major~0, so a
|
||||
major-0 reader must keep parsing them as \texttt{u64}. Unifying them would
|
||||
require bumping the manifest itself, which this data-model bump deliberately
|
||||
avoids; that unification waits for a manifest-revising major.
|
||||
|
||||
\subsection{Migration from major~0}
|
||||
The v0${\to}$v1 translation is total and default-filling: every new field has
|
||||
a canonical default, so no value is unrecoverable --- including the
|
||||
\texttt{CreateRegion} operation payload, whose embedded \texttt{Region}
|
||||
default-fills structurally. This is unlike the \emph{semantic},
|
||||
context-dependent operation-payload migration of Section~\ref{sec:ops:v0}
|
||||
(which could be irreversible); the major-1 migration needs no score context.
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{p{1.9in} p{3.4in}}
|
||||
\toprule
|
||||
\textbf{major-0 form} & \textbf{major-1 form} \\
|
||||
\midrule
|
||||
\texttt{Canvas} $=$ \texttt{regions} &
|
||||
append \texttt{layout\_defaults} $=$ the A4/8\,mm default (page
|
||||
$105 \times 148.5$, margins $7.5$ staff spaces) \\
|
||||
\texttt{Instrument} $=$ \texttt{id}, \texttt{name} &
|
||||
append \texttt{range} $=$ \texttt{None} (presence byte \tablenums{0}) \\
|
||||
\texttt{Region} $= \ldots$ (snapshot) &
|
||||
append \texttt{permits\_spanning\_slurs} $=$ \tablenums{0}
|
||||
(\texttt{false}) \\
|
||||
\texttt{CreateRegion} op payload &
|
||||
default-fill the embedded \texttt{Region}'s appended field
|
||||
(\texttt{permits\_spanning\_slurs} $=$ \tablenums{0}); \emph{canonical} ---
|
||||
a v0 op block migrates on read, and a block bearing a v1 \texttt{CreateRegion}
|
||||
is major~1 (a major-0-only reader opens the bundle read-only) \\
|
||||
\texttt{ResolvedLayoutIR} \texttt{u64} prefixes &
|
||||
non-canonical \texttt{LayoutCache}: discard and regenerate at major~1
|
||||
under \texttt{u32} (no in-place byte translation) \\
|
||||
barrier / extension-declaration blobs &
|
||||
stay regime~(b) \texttt{u64} (manifest-embedded, manifest stays major~0);
|
||||
unchanged in this bump \\
|
||||
canonical-base \texttt{MaterializedState} &
|
||||
unchanged, byte-identical, stays major~0 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
A reader migrates a major-0 acceleration \texttt{Score} snapshot on read
|
||||
(cheaper than replaying the operation log); a writer emits only major-1 forms
|
||||
for the changed payloads and leaves the canonical base at major~0.
|
||||
|
||||
% ===========================================================================
|
||||
\chapter{Non-Canonical Pinned Encodings}
|
||||
|
|
@ -2321,9 +2519,13 @@ $\texttt{LayoutObjectId} = \mathrm{trunc128}(\mathrm{BLAKE3}(
|
|||
|
||||
The resolved layout's canonical bytes are the \textbf{byte-equal conformance
|
||||
surface} of core Chapter~7: two conforming engravers given the same score,
|
||||
profile, and glyph catalog must produce identical bytes. The encoding is
|
||||
regime (b) --- \texttt{u64} LE counts and length prefixes --- with every
|
||||
geometric coordinate quantized to the $1/1024$ staff-space grid as a
|
||||
profile, and glyph catalog must produce identical bytes. In schema major~0 the
|
||||
encoding is regime (b) (\texttt{u64} LE counts and length prefixes); schema
|
||||
major~1 moves it to regime (a)'s \texttt{u32} prefixes with the rest of the
|
||||
unification (Section~\ref{sec:evolution:major1}), re-locking this surface's
|
||||
goldens. Because the surface is non-canonical, a reader regenerates a
|
||||
foreign-major layout cache rather than decoding it. Either way every geometric
|
||||
coordinate is quantized to the $1/1024$ staff-space grid as a
|
||||
\texttt{QuantizedCoord} (8 LE bytes; Chapter~\ref{ch:primitives});
|
||||
a non-finite or out-of-range coordinate is a determinism violation and
|
||||
\MUST{} be rejected, never normalized. The top-level section order is:
|
||||
|
|
@ -2556,6 +2758,23 @@ layouts of Section~\ref{sec:values:representative}.
|
|||
assignment changed. Semantics: Operation Catalog 0.5.0 (\sectionsc{CreateStaff},
|
||||
\sectionsc{Meter and Tempo Overwrites}, \sectionsc{SetStaffLayout}, and the
|
||||
value-restoring \sectionsc{UndoTransaction} revision). \\
|
||||
\today & Schema evolution / Graph value layouts & 0.3.0 --- Defines
|
||||
\textbf{schema major~1}, the first data-model expansion major
|
||||
(Section~\ref{sec:evolution:major1}): appends \texttt{Canvas.layout\_defaults}
|
||||
(new \texttt{CanvasLayoutDefaults}/\texttt{CanvasSize}/\texttt{CanvasMargins}
|
||||
layouts, snapshot-only), \texttt{Instrument.range} (\texttt{Option<PitchRange>},
|
||||
snapshot-only), and \texttt{Region.permits\_spanning\_slurs} (also inside the
|
||||
\emph{canonical} \texttt{CreateRegion} operation payload); narrows regime~(b)
|
||||
by unifying the non-canonical resolved-layout length prefixes to \texttt{u32}
|
||||
(the manifest-embedded barrier blobs stay \texttt{u64}, partly resolving the
|
||||
standing open question); pins per-payload-type major assignment (the
|
||||
canonical-base \texttt{MaterializedState} stays major~0, byte-identical),
|
||||
the accept-set gate ($[\textsc{min},\textsc{max}]$), the cross-major reader
|
||||
rules (discard-and-regenerate non-canonical chunks; parse-or-read-only for
|
||||
canonical ones, so a major-0 reader opens a bundle with v1 \texttt{CreateRegion}
|
||||
ops read-only), and the total default-filling v0${\to}$v1 migration table.
|
||||
Clarifies that any field add is a major change regardless of
|
||||
\texttt{Option}-ness. \\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -883,6 +883,27 @@ the other in general.
|
|||
storage makes the pitch self-describing.
|
||||
\end{rationale}
|
||||
|
||||
\begin{lstlisting}[language=Rust]
|
||||
/// A closed range of pitches, used for advisory declarations: an
|
||||
/// instrument's playable compass and an indeterminate event's pitch hint.
|
||||
/// Both bounds are full pitches.
|
||||
pub struct PitchRange {
|
||||
pub lowest: Pitch,
|
||||
pub highest: Pitch,
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
A \texttt{PitchRange} is \emph{advisory}. Whether a given pitch lies within
|
||||
it is decidable only when the pitch and both bounds share a pitch space that
|
||||
defines an order (the common CMN case); cross-space or partially-ordered
|
||||
comparisons are treated as ``not out of range'' --- a sound-but-incomplete
|
||||
check, as with region time overlap. A range is well-formed when
|
||||
\texttt{lowest} does not sort above \texttt{highest} in a common ordered
|
||||
space. A solver \MAY{} flag an out-of-range pitch but \MUSTNOT{} treat the
|
||||
range as a hard constraint. The type is defined with schema major~1, closing
|
||||
the gap in which \texttt{IndeterminacyHints} and \texttt{Instrument} named a
|
||||
\texttt{PitchRange} that no section defined.
|
||||
|
||||
\section{Scale Position}
|
||||
\label{sec:pitch:scale-position}
|
||||
|
||||
|
|
@ -4228,8 +4249,40 @@ pub struct Canvas {
|
|||
/// system layout, page-break behavior.
|
||||
pub layout_defaults: CanvasLayoutDefaults,
|
||||
}
|
||||
|
||||
/// Canvas-level layout defaults. Page size and margins are in staff
|
||||
/// spaces; a solver reads them as its default page geometry and MAY
|
||||
/// override per solve. The type is defined with schema major 1 (its field
|
||||
/// was named on the canvas from the first draft; only its type was
|
||||
/// undefined).
|
||||
pub struct CanvasLayoutDefaults {
|
||||
pub page_size: CanvasSize,
|
||||
pub margins: CanvasMargins,
|
||||
}
|
||||
|
||||
/// A page size in staff spaces (1 staff space = staff height / 4).
|
||||
pub struct CanvasSize {
|
||||
pub width: f64,
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
/// Page margins in staff spaces.
|
||||
pub struct CanvasMargins {
|
||||
pub top: f64,
|
||||
pub right: f64,
|
||||
pub bottom: f64,
|
||||
pub left: f64,
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
The default is \textbf{A4 portrait at an 8\,mm staff} (1 staff space =
|
||||
2\,mm): page $105 \times 148.5$ staff spaces, $7.5$-staff-space margins,
|
||||
hence a $90 \times 133.5$ content area. Each staff-space scalar is realized
|
||||
canonically as a \texttt{CanonicalF64} leaf; the reference engraver maps a
|
||||
\texttt{CanvasLayoutDefaults} onto its internal page geometry. This closes
|
||||
the long-standing gap in which the canvas named \texttt{layout\_defaults} but
|
||||
no chapter defined \texttt{CanvasLayoutDefaults}.
|
||||
|
||||
\subsection{Regions}
|
||||
|
||||
\begin{lstlisting}[language=Rust]
|
||||
|
|
@ -4254,6 +4307,12 @@ pub struct Region {
|
|||
/// Optional tempo and tuning overrides scoped to this region.
|
||||
pub local_tempo_map: Option<TempoMap>,
|
||||
pub local_tuning_overrides: Vec<TuningOverride>,
|
||||
|
||||
/// Whether spanners (slurs first of all) may cross this region's
|
||||
/// boundary into an adjacent region. Default false: cross-region
|
||||
/// spanning is not permitted unless a region opts in. Added with
|
||||
/// schema major 1.
|
||||
pub permits_spanning_slurs: bool,
|
||||
}
|
||||
|
||||
pub enum RegionContent {
|
||||
|
|
@ -10452,9 +10511,17 @@ pub struct SchemaVersion {
|
|||
\item Minor version changes are backward-compatible. A reader
|
||||
supporting major version $N$ \MUST{} accept any minor version
|
||||
$N.\textit{m}$ for $m \leq$ the reader's supported minor.
|
||||
Minor version changes \MUST{} only add optional fields or
|
||||
enumeration variants in a way preserving the wire format of
|
||||
prior values.
|
||||
Under the positional, frozen-layout wire form, a minor change
|
||||
\MUST{} only \emph{append discriminants to the append-safe
|
||||
vocabularies the Binary Format companion defines} (its open
|
||||
\texttt{Registered} vocabularies, plus ratified appends to
|
||||
\texttt{OperationKind}, \texttt{OperationKindTag},
|
||||
\texttt{OperationPayload}, and the closed value-layer unions), which
|
||||
leaves the bytes of every prior value untouched; it \MUSTNOT{} add or
|
||||
reorder a struct field --- adding a field, even an \texttt{Option},
|
||||
occupies a new positional slot and shifts subsequent bytes, so it is a
|
||||
\emph{major} change (Binary Format companion,
|
||||
\sectionsc{What ``Additive'' Means Here}).
|
||||
\item The manifest's \texttt{manifest\_schema\_version} declares
|
||||
the schema of the manifest itself. Each chunk's
|
||||
\texttt{schema\_version} field declares the schema of that
|
||||
|
|
@ -10465,6 +10532,26 @@ pub struct SchemaVersion {
|
|||
\end{itemize}
|
||||
\end{requirement}
|
||||
|
||||
Schema major~1 is the first data-model expansion major: it adds
|
||||
\texttt{Canvas.layout\_defaults}, \texttt{Instrument.range}, and
|
||||
\texttt{Region.permits\_spanning\_slurs} to the graph, and unifies the
|
||||
non-canonical resolved-layout length-prefix width. Its wire form and the
|
||||
byte-for-byte migration from major~0 are defined in the Binary Format
|
||||
companion. The canonical-base \texttt{MaterializedState} embeds none of these
|
||||
values, so it is byte-identical across the bump and stays major~0.
|
||||
\texttt{Canvas.layout\_defaults} and \texttt{Instrument.range} reach only the
|
||||
non-canonical acceleration (full-\texttt{Score}) snapshot; but
|
||||
\texttt{Region.permits\_spanning\_slurs} also reaches the \emph{canonical}
|
||||
operation layer, because \texttt{CreateRegion} embeds a full \texttt{Region},
|
||||
so an operation-envelope block bearing a v1 \texttt{CreateRegion} is major~1.
|
||||
Consequently a major-0-only reader opens a major-1 bundle \emph{fully} only
|
||||
when the bundle carries no v1 \texttt{CreateRegion} operation; otherwise it
|
||||
reads the major-0 canonical base and manifest but opens read-only (it cannot
|
||||
replay the v1 canonical operations), per the canonical/non-canonical rules
|
||||
above. It always discards the higher-major non-canonical caches. A major-1
|
||||
reader migrates a major-0 acceleration snapshot and a major-0
|
||||
\texttt{CreateRegion} payload on read (default-filling the new fields).
|
||||
|
||||
\section{Format Profiles}
|
||||
\label{sec:format:profiles}
|
||||
|
||||
|
|
@ -14578,6 +14665,23 @@ layouts they own versus inherit:
|
|||
score/canvas slots remain deliberately unavailable pending an
|
||||
addressable root model (P12-K8).
|
||||
\\
|
||||
\today & Schema major 1 (data-model expansion) &
|
||||
Defines the two referenced-but-undefined types
|
||||
\texttt{CanvasLayoutDefaults} (with \texttt{CanvasSize} /
|
||||
\texttt{CanvasMargins}, staff-space page geometry, A4/8\,mm default;
|
||||
closes P12-I7) and \texttt{PitchRange} (advisory pitch compass, used by
|
||||
\texttt{Instrument.range} and \texttt{IndeterminacyHints}); adds
|
||||
\texttt{Region.permits\_spanning\_slurs} (default false, P12-K7). Records
|
||||
that schema major~1 is the first data-model expansion major: the
|
||||
canonical-base \texttt{MaterializedState} embeds none of these values and
|
||||
stays major~0, byte-identical, but \texttt{Region.permits\_spanning\_slurs}
|
||||
reaches the \emph{canonical} \texttt{CreateRegion} operation payload, so a
|
||||
major-0 reader opens a bundle carrying v1 \texttt{CreateRegion} ops
|
||||
read-only. The wire form, the resolved-layout length-prefix unification
|
||||
(the manifest-embedded barrier blobs stay \texttt{u64}), and the
|
||||
byte-for-byte v0${\to}$v1 migration are ratified in the Binary Format
|
||||
companion (0.2.0 $\rightarrow$ 0.3.0).
|
||||
\\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -515,11 +515,12 @@ assembles its \texttt{SolveReport}:
|
|||
\item the solve's constrained input $C$ (a \texttt{ConstrainedLayoutIR}:
|
||||
horizontal spring slots, vertical bands, declared constraints);
|
||||
\item the declared page geometry the solve was configured with: the
|
||||
content width $W$ and content height $H$, in staff spaces. (The score
|
||||
graph has no home for page geometry yet --- the core names
|
||||
\texttt{Canvas.layout\_defaults} without defining it, tracked as
|
||||
Pass-12 row P12-I7 --- so the geometry is a solver parameter, and the
|
||||
Reference Suite companion requires each suite entry to declare it.)
|
||||
content width $W$ and content height $H$, in staff spaces. (Schema
|
||||
major~1 defines \texttt{Canvas.layout\_defaults} and its type
|
||||
\texttt{CanvasLayoutDefaults}, P12-I7; the reference implementation's
|
||||
code graph home lands in a later phase, so until then the geometry is a
|
||||
solver parameter, and the Reference Suite companion requires each suite
|
||||
entry to declare it.)
|
||||
\end{enumerate}
|
||||
|
||||
Notation used throughout Chapter~\ref{ch:metrics}:
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -410,10 +410,10 @@ under:
|
|||
documented default --- A4 portrait at an 8\,mm staff height: page
|
||||
$105 \times 148.5$ staff spaces, margins $7.5$ staff spaces on all
|
||||
four sides, hence a content area of $90 \times 133.5$ staff spaces.
|
||||
(The score graph has no home for page geometry yet: the core names
|
||||
\texttt{Canvas.layout\_defaults} without defining a type, tracked as
|
||||
Pass-12 row P12-I7, so geometry is declared per entry as a solver
|
||||
parameter.)
|
||||
(Schema major~1 defines \texttt{Canvas.layout\_defaults} and its type
|
||||
\texttt{CanvasLayoutDefaults} (P12-I7); the reference implementation's
|
||||
code graph home lands in a later phase, so until then geometry is
|
||||
declared per entry as a solver parameter.)
|
||||
\item the \textbf{solver configuration}: the \texttt{SolverConfig}
|
||||
fields. In v0.1 every entry uses the default configuration --- the
|
||||
\texttt{Standard} profile, an unbounded deterministic budget, and
|
||||
|
|
|
|||
Loading…
Reference in New Issue