diff --git a/CONFORMANCE.md b/CONFORMANCE.md index ce4cf6c..9c2e206 100644 --- a/CONFORMANCE.md +++ b/CONFORMANCE.md @@ -107,11 +107,14 @@ substituting a vendor heuristic: - **Spelling**: `SpellingAlgorithmId` `"default"` — a Temperley-style line-of-fifths preference algorithm, v1 (`epiphany-core/src/prepass.rs`). - The identifier is the crate's proposal pending ratification (P12-H1). A - profile requesting any other id errors; nothing is silently substituted. + The identifier is ratified normative (Pass 12, + `req:pitch:spelling-algorithm`). A profile requesting any other id errors; + nothing is silently substituted. - **Notational decomposition**: `DecompositionAlgorithmId` `"default"` — the - integer-grid metric splitter, v1, with its scope bounds recorded as - P12-H4 (single governing meter, `MAX_DOTS = 1`, tuplet-nesting deferred). + integer-grid metric splitter, v1, with its scope bounds ratified as + normative bounds of the versioned algorithm (Pass 12, + `req:time:decomposition-algorithm`: single governing meter, `MAX_DOTS = 1`, + tuplet-nesting deferred; a wider algorithm is a version bump). Same no-substitution rule. - Both pre-pass outputs are **canonical derived annotations**: deterministic functions of `(materialized graph, profile, algorithm id)` recomputed on diff --git a/crates/epiphany-core/DECISIONS.md b/crates/epiphany-core/DECISIONS.md index 9c881a7..65bb1cd 100644 --- a/crates/epiphany-core/DECISIONS.md +++ b/crates/epiphany-core/DECISIONS.md @@ -416,3 +416,25 @@ the continued-fraction approximation. Note the class's *non-normative* unit label is "wallclock seconds" while this residual is measured in whole notes; the class identity (`TempoIntegration`: conversion residual in either direction) is what is normative. + +## Pass 12 G-pass (2026-07-07): the H rows are ratified + +All seven H rows are retired (dispositions in +`spec/PASS12_RATIFICATION_LOG.md`, "G-pass tranche"; worklist +`spec/PASS12_WORKLIST.md`). Summary: **H1** `"default"` = Temperley +line-of-fifths v1 is ratified normative (`req:pitch:spelling-algorithm`); +**H3** convention-as-tiebreak and **H5** region-time-model-independence are +pinned as properties of that versioned algorithm; **H4** the five +decomposition bounds are the declared normative bounds of `"default"` v1 +(`req:time:decomposition-algorithm`; C5's derived-notation gap is subsumed); +**H6** decomposition precedence is ratified FIXED (not configurable — a +configurable order would be a schema-major `Score` field with no consumer); +**H2** narrowed (the content model landed in I-0; key-aware spelling is +algorithm-v2 territory, cancelling naturals a notation refinement); **H7** +decided the other way from the implementation: authored attachments on +inference-ineligible targets MUST surface in derived annotations +(`req:pitch:authored-uninferred`) — the code change lands with the G-pass +code tranche (authored-only resolution paths + distinct taxonomy buckets in +both pre-passes). Also ratified here: system-derived intrinsic content is +immutable under reduction (P12-K3; core Ch5 states it, the catalog pins the +precondition, `epiphany-ops` implements). diff --git a/crates/epiphany-editor-core/DECISIONS.md b/crates/epiphany-editor-core/DECISIONS.md new file mode 100644 index 0000000..9973833 --- /dev/null +++ b/crates/epiphany-editor-core/DECISIONS.md @@ -0,0 +1,22 @@ +# epiphany-editor-core — Decisions + +Decision log for the headless editing seam (`EditorSession`). Older decisions +for this crate were recorded in session notes and the Pass-12 batch rows it +filed (P12-E4, P12-E5); this file starts with their ratification. + + +## Pass 12 G-pass (2026-07-07): E4/E5 are ratified + +Dispositions in `spec/PASS12_RATIFICATION_LOG.md` ("G-pass tranche"). +**E4** adopt-as-implemented (`req:format:barrier-matching`): target-free +operations (`SetMetadata`, `DeclareTransaction`) are matched by score-wide +barriers only; opaque `Registered` operations match fully conservatively. +**E5** semantics ratified (`req:format:unsafe-tombstone`): crossing a barrier +immediately deactivates the extension's remaining barriers; the crossing MUST +be durably recorded at the next commit; a tombstoned `required = true` +extension leaves the bundle read-only for dependents. The manifest-side byte +encoding is deferred to the Binary Format companion (new open question there: +the manifest is frozen at major 0, so the record rides the blob layer or a +new chunk kind — next bundle-format tranche); +`extensions_requiring_tombstone()` remains the producer awaiting that +consumer. diff --git a/crates/epiphany-layout-ir/DECISIONS.md b/crates/epiphany-layout-ir/DECISIONS.md index 9edd388..debb403 100644 --- a/crates/epiphany-layout-ir/DECISIONS.md +++ b/crates/epiphany-layout-ir/DECISIONS.md @@ -465,3 +465,18 @@ schema-major-1 tranche): `(original, ordinal)` `stable_semantic_instance_key`. Since `LayoutObjectId`s are non-canonical and re-derived per layout, the key need only be stable within a layout. Ratified as core spec `req:layoutir:continuation-synthesis`. + +## Pass 12 G-pass (2026-07-07): I4/I5/I6 are ratified + +Dispositions in `spec/PASS12_RATIFICATION_LOG.md` ("G-pass tranche"), all +adopt-as-implemented; these are deliberate Standard-tier design inputs. +**I4** strength is kind-determined (`req:solver:kind-strength`): no instance +strength field; breaks by `BreakKind` (Hard→Required, Soft→Preferred{1.0}), +other core families Required, `Registered` conservative Required; future +constraint families declare their strength in their normative definitions. +**I5** the stub's constraints-present-but-unevaluated report +(`SolvedWithWarnings` + `satisfied_hard_constraints == false` + warning) is +sanctioned (`req:solver:subconformant-report`). **I6** the implemented +emission set (successive-notehead no-collision chains + per-glyph containment ++ user-break constraints) is the normative Minimal-tier floor +(`req:layoutir:constraint-floor`). diff --git a/crates/epiphany-ops/DECISIONS.md b/crates/epiphany-ops/DECISIONS.md index c710cfe..187d1c2 100644 --- a/crates/epiphany-ops/DECISIONS.md +++ b/crates/epiphany-ops/DECISIONS.md @@ -916,3 +916,27 @@ of the same transaction could be defined as idempotent rather than conflicting; (4) P12-C5 stands as filed (the decomposition pre-pass still honors only the first governing meter — the reduction semantics are pinned here and tested under `a_mid_region_meter_change_reduces_cleanly_p12_c5`). + +## Pass 12 G-pass (2026-07-07): the K and C rows are ratified + +All open K/C rows are retired (dispositions in +`spec/PASS12_RATIFICATION_LOG.md`, "G-pass tranche"). Adopted as implemented: +**K1** (migration read-only fallback is long-term), **K4** (earliest resolve +governs universally — no supersede; re-resolution is a future dedicated op; +no Conflict object kind), **K6** (single-pass promotion, quarantined resolves +never govern, pending resolves govern set-level, `TargetMissing` reuse kept), +**K10** (`TransactionConflict` reuse blessed for strand-blocks), **K11** +(undo idempotence asymmetry is normative), **K12** (cross-region slur = AND), +**C1** (cue cascades on any source death), **C2** (Range truncate = region +edge, zero offset), **C3** (annotation orphaning sanctioned), **C5** (folded +into H4's single-meter bound). Deferred with named sites: **K2** (tuning +catalog; prototype pinned as declared v1 behavior, replacement = payload +schema-major), **K5** (Profile Conformance companion; v1 profiles declare no +selection function). Decided with code to land in the G-pass code tranche: +**K3** refuse SYSTEM_DERIVED intrinsic-content rewrites +(`PreconditionFailureReason::SystemDerivedContentImmutable`, discriminant 12), +**K9** differing-value re-creates get `RecreateContentMismatch` (13), +**C4** rank-4 re-anchors get `ReanchorReason::SameCanvasNearer` (6; 5 was +already `DeclaredByExtension`). **K8** retired: genesis is outside the +operation set (catalog K1 slots removed, core Ch5 states it). Catalog +0.5.0 → 0.6.0; Binary Format 0.3.0 → 0.4.0. diff --git a/spec/PASS12_BATCH.md b/spec/PASS12_BATCH.md index 0298441..684ce70 100644 --- a/spec/PASS12_BATCH.md +++ b/spec/PASS12_BATCH.md @@ -5,7 +5,9 @@ not into code improvisations. … Don't open Pass 12 until at least 3 items accumulate."* -**Status: OPEN.** Agent H's landing (spelling + decomposition pre-passes) +**Status: CLOSED (G-pass, 2026-07-07).** Every row below is struck: the G-ratification pass (`PASS12_WORKLIST.md`, dispositions in `PASS12_RATIFICATION_LOG.md` "G-pass tranche") retired the full batch. New Pass-13 candidates open a fresh file. + +**Original status: OPEN.** Agent H's landing (spelling + decomposition pre-passes) surfaced five candidates, crossing the ≥3 threshold. This file is the running collection; G ratifies (or defers/rejects) the batch when Phase 2's open questions are resolved. F collects, F does not resolve. @@ -28,41 +30,41 @@ code instead is the failure mode this batch exists to prevent. | ID | Source | Summary | Disposition target | |----|--------|---------|--------------------| -| P12-H1 | `epiphany-core` H | Ratify `SpellingAlgorithmId::Default` = Temperley line-of-fifths v1 (Pass 11 closed before H landed; the id `"default"` is the crate's proposal until ratified — not a byte layout, so nothing golden-locks on it). | G (algorithm-choice ratification) | -| P12-H2 | `epiphany-core` H | `KeySignatureChange` / `ClefChange` are anchor-only placeholders; context-aware spelling infers tonal context from the melody (line-of-fifths centre of gravity) rather than a *declared* key. A real key/clef content model would let spelling/decomposition honour declared keys and place cancelling naturals. Flagged as a graph-model gap. | G (graph model) | -| P12-H3 | `epiphany-core` H | Chromatic-run convention (ascending = sharps, descending = flats) is only a *tiebreak* in the centre-of-gravity rule, so an isolated chromatic run with no tonal context may pick the enharmonic the convention would not. Voice-leading refinement deferred. | G / Pass 12 (spelling) | -| P12-H4 | `epiphany-core` H | Decomposition simplifications: single governing meter per region (multi/mid-region meter changes deferred); region origin assumed a barline (anacrusis deferred); compound-meter beat grouping beyond the dyadic default; tuplet nesting and cross-beat tuplet members; double+ augmentation dots (`MAX_DOTS = 1`). | G (decomposition scope) | -| P12-H5 | `epiphany-core` H | Automatic spelling under aleatoric regions (the spec's open question). H spells pitches region-independently but performs no region-specific aleatoric spelling; defer if the algorithm does not generalise cleanly. | G / Pass 12 (open question) | +| ~~P12-H1~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Ratify `SpellingAlgorithmId::Default` = Temperley line-of-fifths v1 (Pass 11 closed before H landed; the id `"default"` is the crate's proposal until ratified — not a byte layout, so nothing golden-locks on it). | G (algorithm-choice ratification) | +| ~~P12-H2~~ **DEFERRED-NARROWED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | `KeySignatureChange` / `ClefChange` are anchor-only placeholders; context-aware spelling infers tonal context from the melody (line-of-fifths centre of gravity) rather than a *declared* key. A real key/clef content model would let spelling/decomposition honour declared keys and place cancelling naturals. Flagged as a graph-model gap. | G (graph model) | +| ~~P12-H3~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Chromatic-run convention (ascending = sharps, descending = flats) is only a *tiebreak* in the centre-of-gravity rule, so an isolated chromatic run with no tonal context may pick the enharmonic the convention would not. Voice-leading refinement deferred. | G / Pass 12 (spelling) | +| ~~P12-H4~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Decomposition simplifications: single governing meter per region (multi/mid-region meter changes deferred); region origin assumed a barline (anacrusis deferred); compound-meter beat grouping beyond the dyadic default; tuplet nesting and cross-beat tuplet members; double+ augmentation dots (`MAX_DOTS = 1`). | G (decomposition scope) | +| ~~P12-H5~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Automatic spelling under aleatoric regions (the spec's open question). H spells pitches region-independently but performs no region-specific aleatoric spelling; defer if the algorithm does not generalise cleanly. | G / Pass 12 (open question) | | ~~P12-I1~~ **RESOLVED (I-1)** | `epiphany-layout-ir` / `engrave` / `render-svg` I | The v0 pipeline was a **structural placeholder** (one *arbitrary* glyph per object at `y = 0`), not real notation. **Resolved by I-1 (Phase 2-3):** `to_constrained` now builds real notation — clef-relative noteheads (by `NoteValue`), spelling-derived accidentals, key/time signatures, rests, barlines, and the staff-line/stem strokes — and the Engraver re-spaces it; the human visual-acceptance gate is met (goldens locked against the stub *and* the real Engraver). The Ch 7 engraving-boundary question resolved to: notation construction lives in `to_constrained`, horizontal spacing in the Engraver. | ✅ done | | ~~P12-I2~~ **RESOLVED (wired)** | `epiphany-determinism` / `epiphany-layout-ir` I | Stable layout-object id derivation (`MUSCLOID`, Pass-11 item 2.6). **Wired:** `epiphany-determinism` now reserves the built-in `DomainTag::LAYOUT_OBJECT_ID` (`MUSCLOID`), and `layout-ir`'s provenance derivations (single / multiply-manifested / synthesized) plus the engraving-decision id route through it (no longer borrowing `MUSCCONF`). Layout ids stay non-canonical, so only `data-prov` hex in the render goldens changed; no durable/interchanged artifact. See `layout-ir/DECISIONS.md` and `req:layoutir:object-id-derivation`. | ✅ done | | ~~P12-I3~~ **RESOLVED (I-4a)** | `epiphany-layout-ir` I | The bundled `BRAVURA_METRICS` were *approximations* disagreeing with the renderer's genuine outlines. **Resolved by I-4a:** the metrics table is re-extracted from the **same** SHA-pinned `bravura-1.392` font the outlines come from, with bboxes rounded *outward* so each metric box contains the drawn ink (a `render-svg` test proves containment); `BRAVURA_VERSION = SemVer(1, 392, 0)`. A coupled barline-placement bug it surfaced (bottom-origin glyph floated) was fixed in the same increment. | ✅ done | -| P12-K1 | `epiphany-ops` K | A v0 `RespellPitch` carried a `ContentHash` *fingerprint* of the spelling, not the `PitchSpelling`. The v0→v1 migration (Operation Catalog, M1) cannot invert a fingerprint, so it recovers the spelling from the score-graph context (an explicit per-pitch spelling attachment whose canonical bytes hash to the fingerprint) and returns `MigrationError::Irreversible` (bundle opens read-only) when the context lacks it. Every other representative payload migrates self-contained; this is the lone exception. Confirm the read-only fallback is the intended disposition vs. requiring a v0 corpus that preserves spelling pre-images. | G / Pass 12 (migration) | -| P12-K2 | `epiphany-ops` K | The `Transpose` op (Operation Catalog, M2 Group 1) carries a minimal `chromatic_steps: i32` interval and `reduce_onto` applies it as a CMN *alteration* shift only. Faithful interval algebra (diatonic vs. chromatic intervals, octave/nominal renormalization, transposition in non-CMN pitch spaces) is the deferred Chapter 4 tuning-catalog territory. The prototype also clamps the shifted alteration to the `i8` range, so an extreme transpose silently saturates instead of renormalizing — another reason the representation needs pinning. Pin the interval representation and transposition semantics when the tuning catalog lands. | G / Pass 12 (tuning) | -| P12-H6 | `epiphany-core` H | Decomposition-attachment precedence: the spec says the decomposition pre-pass uses the "same precedence machinery" as spelling, but spelling's machinery is per-score *configurable* with `priority`/timestamp tie-breaks while `DecompositionAttachment` carries no `priority` and the graph has no `DecompositionPrecedence` field. The audit fix implements the fixed default order (`UserChosen > Imported > Propagated > Inferred`, canonical attachment order as tie-break). Decide whether decomposition precedence is configurable (a canonical-codec change) or fixed. | G / Pass 12 (decomposition) | -| P12-H7 | `epiphany-core` H | Authored decompositions for inference-ineligible events: an authored attachment is exactly how a user would notate an *ungriddable* event, but the derived-annotation surface (mirroring spelling, which likewise ignores attachments on spelling-unavailable pitches) only resolves overrides where inferred output exists. Needs a spec answer for both pre-passes. | G / Pass 12 (pre-passes) | -| P12-K3 | `epiphany-ops` K | Content modification of a `SYSTEM_DERIVED` pitch: `ModifyEvent`/`ModifyIdentifiedPitch` can rewrite a synthetic pitch's intrinsic content in place, silently invalidating the id's content-derivation (Invariant 11). The new reduction-time collision check deliberately does not treat in-place rewrites as mints. Decide whether reduction must refuse them outright. | G / Pass 12 (identity) | -| P12-K4 | `epiphany-ops` K | `ResolveConflict` beyond the concurrent case: the spec pins outcomes only for *concurrent* differing resolves; the implementation applies the same rule to causally-later resolves (so an intentional re-resolution cannot supersede) and reads `AlreadyApplied` for any resolve against a `Dismissed` conflict. Also: the meta-conflict record cannot name the contested conflict in `affected_objects` because `TypedObjectId` has no Conflict kind. Pin the causally-later semantics and decide whether conflict records need an addressable object kind. | G / Pass 12 (conflict resolution) | -| P12-C1 | `epiphany-ops` C | Multi-source cue re-anchoring: the rule table's action is plain "cascade-delete" (implemented: any source deletion cascades, like Tie) but its rationale ("a cue with no source is meaningless") implies truncate-while-any-source-survives. Pin one reading. | G / Pass 12 (re-anchoring) | -| P12-C2 | `epiphany-ops` C | Graphic-gesture Range "truncate" is underdetermined. Implemented reading: a dead event-anchored range endpoint moves to its containing region's edge (start→Start, end→End, zero offset). Define "truncate" normatively. | G / Pass 12 (re-anchoring) | -| P12-C3 | `epiphany-ops` C | Analytical-annotation range reconstruction: a wall-clock (region-relative) or indeterminate event span cannot be expressed as a stored `Range` anchor without region-origin resolution, so such annotations orphan. State whether orphaning is the sanctioned outcome or an expressible form is required. | G / Pass 12 (re-anchoring) | -| P12-C4 | `epiphany-ops` C | `ReanchorReason` has no same-canvas variant: a rank-4 (same-canvas) survivor is recorded `ExplicitFallback` rather than appending a discriminant. Ratify a beyond-region reason or bless the fallback recording. | G / Pass 12 (re-anchoring) | -| P12-K5 | `epiphany-ops` K | Equivocation resolution's third path — a profile-declared deterministic selection function (e.g. lowest-hash-wins) — is unpinned and unimplemented; the reducer has no policy hook. Referenced from the catalog §ResolveEquivocation rationale. | G / Pass 12 (equivocation) | -| P12-K6 | `epiphany-ops` K | `ResolveEquivocation` edge semantics to pin: cascaded resolves (a promoted candidate that is itself a resolve does not govern a further promotion — single-pass implemented, vs. fixpoint); interaction with HLC-monotonicity quarantine (may a quarantined resolve govern?); a resolve held pending by its own causal gaps still governs promotion (set-level rule) while its effect stays pending; whether the invalid-target/chosen no-op warrants a dedicated `PreconditionFailureReason` (implemented: reuses `TargetMissing`). | G / Pass 12 (equivocation) | +| ~~P12-K1~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | A v0 `RespellPitch` carried a `ContentHash` *fingerprint* of the spelling, not the `PitchSpelling`. The v0→v1 migration (Operation Catalog, M1) cannot invert a fingerprint, so it recovers the spelling from the score-graph context (an explicit per-pitch spelling attachment whose canonical bytes hash to the fingerprint) and returns `MigrationError::Irreversible` (bundle opens read-only) when the context lacks it. Every other representative payload migrates self-contained; this is the lone exception. Confirm the read-only fallback is the intended disposition vs. requiring a v0 corpus that preserves spelling pre-images. | G / Pass 12 (migration) | +| ~~P12-K2~~ **DEFERRED (tuning track; pinned) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | The `Transpose` op (Operation Catalog, M2 Group 1) carries a minimal `chromatic_steps: i32` interval and `reduce_onto` applies it as a CMN *alteration* shift only. Faithful interval algebra (diatonic vs. chromatic intervals, octave/nominal renormalization, transposition in non-CMN pitch spaces) is the deferred Chapter 4 tuning-catalog territory. The prototype also clamps the shifted alteration to the `i8` range, so an extreme transpose silently saturates instead of renormalizing — another reason the representation needs pinning. Pin the interval representation and transposition semantics when the tuning catalog lands. | G / Pass 12 (tuning) | +| ~~P12-H6~~ **RATIFIED (fixed order) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Decomposition-attachment precedence: the spec says the decomposition pre-pass uses the "same precedence machinery" as spelling, but spelling's machinery is per-score *configurable* with `priority`/timestamp tie-breaks while `DecompositionAttachment` carries no `priority` and the graph has no `DecompositionPrecedence` field. The audit fix implements the fixed default order (`UserChosen > Imported > Propagated > Inferred`, canonical attachment order as tie-break). Decide whether decomposition precedence is configurable (a canonical-codec change) or fixed. | G / Pass 12 (decomposition) | +| ~~P12-H7~~ **RATIFIED (surface; code tranche) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-core` H | Authored decompositions for inference-ineligible events: an authored attachment is exactly how a user would notate an *ungriddable* event, but the derived-annotation surface (mirroring spelling, which likewise ignores attachments on spelling-unavailable pitches) only resolves overrides where inferred output exists. Needs a spec answer for both pre-passes. | G / Pass 12 (pre-passes) | +| ~~P12-K3~~ **RATIFIED (refuse; code tranche) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | Content modification of a `SYSTEM_DERIVED` pitch: `ModifyEvent`/`ModifyIdentifiedPitch` can rewrite a synthetic pitch's intrinsic content in place, silently invalidating the id's content-derivation (Invariant 11). The new reduction-time collision check deliberately does not treat in-place rewrites as mints. Decide whether reduction must refuse them outright. | G / Pass 12 (identity) | +| ~~P12-K4~~ **RATIFIED (no supersede) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | `ResolveConflict` beyond the concurrent case: the spec pins outcomes only for *concurrent* differing resolves; the implementation applies the same rule to causally-later resolves (so an intentional re-resolution cannot supersede) and reads `AlreadyApplied` for any resolve against a `Dismissed` conflict. Also: the meta-conflict record cannot name the contested conflict in `affected_objects` because `TypedObjectId` has no Conflict kind. Pin the causally-later semantics and decide whether conflict records need an addressable object kind. | G / Pass 12 (conflict resolution) | +| ~~P12-C1~~ **RATIFIED (cascade) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` C | Multi-source cue re-anchoring: the rule table's action is plain "cascade-delete" (implemented: any source deletion cascades, like Tie) but its rationale ("a cue with no source is meaningless") implies truncate-while-any-source-survives. Pin one reading. | G / Pass 12 (re-anchoring) | +| ~~P12-C2~~ **RATIFIED (truncate defined) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` C | Graphic-gesture Range "truncate" is underdetermined. Implemented reading: a dead event-anchored range endpoint moves to its containing region's edge (start→Start, end→End, zero offset). Define "truncate" normatively. | G / Pass 12 (re-anchoring) | +| ~~P12-C3~~ **RATIFIED (orphan sanctioned) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` C | Analytical-annotation range reconstruction: a wall-clock (region-relative) or indeterminate event span cannot be expressed as a stored `Range` anchor without region-origin resolution, so such annotations orphan. State whether orphaning is the sanctioned outcome or an expressible form is required. | G / Pass 12 (re-anchoring) | +| ~~P12-C4~~ **RATIFIED (reason 6; code tranche) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` C | `ReanchorReason` has no same-canvas variant: a rank-4 (same-canvas) survivor is recorded `ExplicitFallback` rather than appending a discriminant. Ratify a beyond-region reason or bless the fallback recording. | G / Pass 12 (re-anchoring) | +| ~~P12-K5~~ **DEFERRED (profile companion) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | Equivocation resolution's third path — a profile-declared deterministic selection function (e.g. lowest-hash-wins) — is unpinned and unimplemented; the reducer has no policy hook. Referenced from the catalog §ResolveEquivocation rationale. | G / Pass 12 (equivocation) | +| ~~P12-K6~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | `ResolveEquivocation` edge semantics to pin: cascaded resolves (a promoted candidate that is itself a resolve does not govern a further promotion — single-pass implemented, vs. fixpoint); interaction with HLC-monotonicity quarantine (may a quarantined resolve govern?); a resolve held pending by its own causal gaps still governs promotion (set-level rule) while its effect stays pending; whether the invalid-target/chosen no-op warrants a dedicated `PreconditionFailureReason` (implemented: reuses `TargetMissing`). | G / Pass 12 (equivocation) | | ~~P12-K7~~ **RATIFIED (schema-major-1 track, 2026-07-06; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | Advisory-precondition catalog: the spec declares the duration-boundary advisory only for InsertEvent while the implementation also applies it to ModifyEvent's replacement value — state ModifyEvent's bucket explicitly. Also: which advisory checks are blocked on graph-model completion (Instrument declared range; a Region slur-spanning permission flag; which extent shapes constitute a resolvable musical end bound). | G / Pass 12 (validation) | -| P12-I4 | `epiphany-layout-ir` I | Constraint-strength attachment: Ch9 defines `ConstraintStrength` and says the solver consumes constraints "in normalized form", but neither the normalized form nor Ch7's `LayoutConstraint` provides a channel for an instance to carry strength. Implemented rule: break strength = `BreakKind` (Hard→Required, Soft→Preferred{1.0}); other core families Required; `Registered` conservative Required. Bless the rule or add a strength field. | G / Pass 12 (solver) | -| P12-I5 | `epiphany-layout-ir` I | No renderable status exists for "constraints present but not evaluated": every renderable `SolveStatus` is documented as "all hard constraints satisfied", leaving a below-conformance passthrough solver no honest report. Implemented encoding: `SolvedWithWarnings` + `satisfied_hard_constraints == false` + a warning. Sanction it or define a non-evaluating-tier report shape. | G / Pass 12 (solver) | -| P12-I6 | `epiphany-layout-ir` I | The spacing pass MUST "build collision constraints" but no per-tier minimum emission set is named. Implemented floor: successive-notehead-column no-collision chains + per-glyph region containment + user-break constraints. A normative Minimal-tier floor would make the acceptance surface testable. | G / Pass 12 (solver) | +| ~~P12-I4~~ **RATIFIED (kind-determined) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-layout-ir` I | Constraint-strength attachment: Ch9 defines `ConstraintStrength` and says the solver consumes constraints "in normalized form", but neither the normalized form nor Ch7's `LayoutConstraint` provides a channel for an instance to carry strength. Implemented rule: break strength = `BreakKind` (Hard→Required, Soft→Preferred{1.0}); other core families Required; `Registered` conservative Required. Bless the rule or add a strength field. | G / Pass 12 (solver) | +| ~~P12-I5~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-layout-ir` I | No renderable status exists for "constraints present but not evaluated": every renderable `SolveStatus` is documented as "all hard constraints satisfied", leaving a below-conformance passthrough solver no honest report. Implemented encoding: `SolvedWithWarnings` + `satisfied_hard_constraints == false` + a warning. Sanction it or define a non-evaluating-tier report shape. | G / Pass 12 (solver) | +| ~~P12-I6~~ **RATIFIED (Minimal floor) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-layout-ir` I | The spacing pass MUST "build collision constraints" but no per-tier minimum emission set is named. Implemented floor: successive-notehead-column no-collision chains + per-glyph region containment + user-break constraints. A normative Minimal-tier floor would make the acceptance surface testable. | G / Pass 12 (solver) | | ~~P12-D1~~ **RESOLVED (Binary Format 0.1.0 §7.6, `req:binfmt:opindex`)** | `epiphany-bundle` D | Operation-index provisional encoding (block-refs + id-sorted entries with u32 block ordinal and u32 in-block offset; golden-locked) awaiting Binary Format companion ratification, together with: the offset's meaning (first content byte within the uncompressed block payload), a normative definition of "stale" (implemented: index block-set ≠ manifest `operation_roots` under full-`ChunkRef` equality), the one-slot-per-id invariant, the load-bearing property that the envelope encoding *leads* with the 16-byte OperationId, and whether the commit-time "grown significantly" SHOULD gets a threshold or stays implementation-defined. Ratified as-implemented; refresh threshold pinned implementation-defined (open question retained in the companion). | ✅ done | | ~~P12-E1~~ **RESOLVED (Binary Format 0.1.0 §8.1, `req:binfmt:ext-blobs`)** | `epiphany-layout-ir` E | Provisional canonical byte form for the `EditBarrier`/`BarrierScope`/`BarrierCondition` tree and the two `ExtensionDeclaration` blobs (`push_set` framing, u64 LE lengths; golden-locked). Ratified as-implemented. | ✅ done | | ~~P12-E2~~ **RESOLVED (Binary Format 0.1.0 §8.2, `req:binfmt:condition-depth`)** | `epiphany-layout-ir` E | The spec places no bound on `BarrierCondition` recursion; the decoder needs one against adversarial bytes. `MAX_CONDITION_DEPTH = 64` implemented — the companion pins 64 as the normative bound (decoders MUST reject deeper; writers MUST NOT emit deeper). | ✅ done | | ~~P12-E3~~ **RESOLVED (Binary Format 0.1.0 §8.1, `req:binfmt:object-kind-open`)** | `epiphany-layout-ir` E | Barrier `ObjectKind` byte form = the `TypedObjectId` 16-bit discriminant (2 LE bytes) with open-value decode (unknown kinds never match, preserving append-only forward compat). Representation and open-value stance ratified. | ✅ done | -| P12-E4 | `epiphany-editor-core` E | Barrier matching for operations with no graph target (`SetMetadata`, `DeclareTransaction` — implemented: score-wide barriers only) and for opaque `Registered` operations (implemented: fully conservative match) is unspecified. | G (Ch. 8) | -| P12-E5 | `epiphany-editor-core` E | The unsafe-edit tombstone MUST has no defined mechanism: the manifest-side form (drop declaration + preserved roots? an explicit tombstone record?), interaction with `required = true`, and whether crossing immediately deactivates the extension's remaining barriers (implemented: yes, recorded via `extensions_requiring_tombstone()` for the next bundle write). | G (Ch. 8) || P12-C5 | `epiphany-ops`/`epiphany-core` C/H | Mid-region meter changes: `SetTimeSignature` (catalog §Meter and Tempo Overwrites) reduces a second `MeterChange` into a region's grid cleanly, but the decomposition pre-pass honours only the first governing meter (P12-H4's single-meter simplification), so derived notation ignores the change until multi-meter decomposition lands. Reduction semantics pinned; the derived-annotation gap is H4's. | G / Pass 12 (decomposition) | -| P12-K8 | `epiphany-ops` K | Create score / canvas remain unavailable K1 slots: the document root and canvas are inline singletons (no `TypedObjectId::Canvas`; the root is never op-minted — genesis is `Score::empty` + bundle creation). Turning them into operations needs an addressable root/canvas object model. Decide: define one, or ratify genesis as deliberately outside the operation set. | G (graph model) | -| 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-K12 | `epiphany-ops` K | Cross-region slur spanning permission: `Region.permits_spanning_slurs` (schema major 1, P12-K7) gates the CreateCrossCutting(Slur) advisory, but which region governs a slur whose endpoints lie in *different* regions is under-specified. Implemented as conservative **AND** (a boundary is permeable only when both endpoint regions permit); "the start region governs" or "either side" are equally defensible. The advisory is authoring-only and never alters reduction, so the choice is not byte-affecting. Ratify one reading. | G / Pass 12 (validation) | +| ~~P12-E4~~ **RATIFIED (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-editor-core` E | Barrier matching for operations with no graph target (`SetMetadata`, `DeclareTransaction` — implemented: score-wide barriers only) and for opaque `Registered` operations (implemented: fully conservative match) is unspecified. | G (Ch. 8) | +| ~~P12-E5~~ **RATIFIED semantics / encoding deferred (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-editor-core` E | The unsafe-edit tombstone MUST has no defined mechanism: the manifest-side form (drop declaration + preserved roots? an explicit tombstone record?), interaction with `required = true`, and whether crossing immediately deactivates the extension's remaining barriers (implemented: yes, recorded via `extensions_requiring_tombstone()` for the next bundle write). | G (Ch. 8) || ~~P12-C5~~ **RATIFIED (folded into H4) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops`/`epiphany-core` C/H | Mid-region meter changes: `SetTimeSignature` (catalog §Meter and Tempo Overwrites) reduces a second `MeterChange` into a region's grid cleanly, but the decomposition pre-pass honours only the first governing meter (P12-H4's single-meter simplification), so derived notation ignores the change until multi-meter decomposition lands. Reduction semantics pinned; the derived-annotation gap is H4's. | G / Pass 12 (decomposition) | +| ~~P12-K8~~ **RATIFIED (genesis outside ops) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | Create score / canvas remain unavailable K1 slots: the document root and canvas are inline singletons (no `TypedObjectId::Canvas`; the root is never op-minted — genesis is `Score::empty` + bundle creation). Turning them into operations needs an addressable root/canvas object model. Decide: define one, or ratify genesis as deliberately outside the operation set. | G (graph model) | +| ~~P12-K9~~ **RATIFIED (reason 13; code tranche) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `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~~ **RATIFIED (reuse blessed) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `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~~ **RATIFIED (asymmetry normative) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `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-K12~~ **RATIFIED (AND) (G-pass 2026-07-07; see PASS12_RATIFICATION_LOG)** | `epiphany-ops` K | Cross-region slur spanning permission: `Region.permits_spanning_slurs` (schema major 1, P12-K7) gates the CreateCrossCutting(Slur) advisory, but which region governs a slur whose endpoints lie in *different* regions is under-specified. Implemented as conservative **AND** (a boundary is permeable only when both endpoint regions permit); "the start region governs" or "either side" are equally defensible. The advisory is authoring-only and never alters reduction, so the choice is not byte-affecting. Ratify one reading. | G / Pass 12 (validation) | | ~~P12-I7~~ **RATIFIED (schema-major-1 track, 2026-07-06; see PASS12_RATIFICATION_LOG)** | `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~~ **RATIFIED (schema-major-1 track, 2026-07-06; core_spec `req:layoutir:break-satisfaction`)** | `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~~ **RATIFIED (schema-major-1 track, 2026-07-06; core_spec `req:layoutir:break-origin-attribution`)** | `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) | diff --git a/spec/PASS12_RATIFICATION_LOG.md b/spec/PASS12_RATIFICATION_LOG.md index fdbb943..95d6655 100644 --- a/spec/PASS12_RATIFICATION_LOG.md +++ b/spec/PASS12_RATIFICATION_LOG.md @@ -103,3 +103,67 @@ conservative **AND** (a boundary is permeable only when both endpoint regions se `permits_spanning_slurs`); a "the start region governs" or "either side" reading is equally defensible. Tracked for ratification; the advisory is authoring-only and never alters reduction, so the choice is not byte-affecting. + +## G-pass tranche (2026-07-07) — the batch pass + +The full G-ratification of the accumulated batch: all 28 open rows retired in +one deliberate pass (worklist: `PASS12_WORKLIST.md`). Four rows carried genuine +forks and were decided by the project lead; three rows defer to *named* landing +sites; the rest bless implemented, DECISIONS-recorded readings. Spec-first: the +normative text landed with this tranche; the small code tranche (H7 surfacing, +K3/K9 reasons, C4 variant) follows in its own commit. + +**Key decisions (project lead, 2026-07-07):** P12-K12 cross-region slur +permission = **AND** (both endpoint regions); P12-H7 authored annotations for +inference-ineligible events **surface** in derived annotations; P12-K4 +ResolveConflict = **no supersede** (earliest applied resolve governs +universally; re-resolution is a future dedicated op; no `TypedObjectId` +Conflict kind); P12-K8 **genesis outside the operation set** (create-score/ +canvas slots retired, not "unavailable"). + +| Item | Disposition | Spec locus | Authority followed | +|---|---|---|---| +| P12-H1 spelling algorithm id | **adopt** — `"default"` = Temperley-style line-of-fifths preference v1, ratified normative (`req:pitch:spelling-algorithm`); profile-declared disposition; no silent substitution. CONFORMANCE.md caveat dropped | core_spec Ch2 §Spelling Pre-Pass; Ch4 open question narrowed; Ch1 + App. D open-hooks lists updated | `epiphany-core/src/prepass.rs` | +| P12-H2 key/clef model | **defer (premise stale, verified)** — the content model *exists* (I-0: `Clef`, `KeySignature`, content-bearing changes) and layout consumes it; what remains is algorithmic. Key-aware spelling → a spelling-algorithm **v2** (versioned rev, deterministically invalidates derived output); key-aware accidental display / cancelling naturals → the notation-refinement backlog (major-2 / Standard-tier neighborhood) | `req:pitch:spelling-algorithm` states v1 does not consult declared keys | verified: 0 `KeySignature` refs in `prepass.rs`; `PlacedKeySignature` in layout-ir | +| P12-H3 chromatic-run convention | **adopt** — enharmonic choice absent tonal context is a property of the *versioned* algorithm; v1 = convention-as-tiebreak only; voice-leading refinement = future version, not a spec hole | `req:pitch:spelling-algorithm` | `prepass.rs` centre-of-gravity rule | +| P12-H4 decomposition scope bounds (+P12-C5 folded) | **adopt** — the five v1 bounds (single governing meter; barline origin; dyadic compound grouping; no nested/cross-beat tuplets; `MAX_DOTS = 1`) become *declared normative bounds* of `DecompositionAlgorithmId "default"` v1 (`req:time:decomposition-algorithm`); a wider algorithm is a version bump. C5: `SetTimeSignature` reduction semantics already pinned (catalog §Meter and Tempo Overwrites); the derived-notation gap is subsumed by the single-meter bound | core_spec Ch3 §Decomposition Pre-Pass | `prepass.rs` integer-grid splitter; ops `reduce.rs` meter LWW | +| P12-H5 aleatoric spelling | **adopt (open question closed)** — spelling is region-time-model-independent; no aleatoric-specific pass exists or is required; stated in the v1 algorithm definition | `req:pitch:spelling-algorithm` | `prepass.rs` (region-independent by construction) | +| P12-H6 decomposition precedence | **decide: FIXED** — the fixed default order (`UserChosen > Imported > Propagated > Inferred`, canonical attachment order tie-break) is ratified; *not* configurable (a configurable order = new canonical `Score` field = schema-major with no consumer). "Same precedence machinery" reworded to "same source-rank discipline" | core_spec Ch3 §Notational Decomposition | `prepass.rs::resolve_decomposition` | +| P12-H7 authored-uninferred surfacing | **decide: SURFACE (code follows)** — derived annotations MUST report the winning authored attachment for inference-ineligible targets, both pre-passes; taxonomy counts them distinctly (`req:pitch:authored-uninferred`). Derived-annotation-only: no canonical-byte impact | core_spec Ch2 (new requirement) + Ch3 cross-ref | decision reverses the implemented override-only mirror; code tranche implements | +| P12-K1 RespellPitch migration | **adopt** — context-recovery + `Irreversible` → read-only is the *long-term* disposition; no richer v0 corpus required (none exists). Open-question box → ratified migration note | operation_catalog §RespellPitch | `epiphany-ops/src/migrate.rs` | +| P12-K2 Transpose algebra | **defer (named site) + pin** — prototype semantics (CMN alteration shift, documented `i8` saturation) declared v1 behavior; faithful interval representation = *payload schema-major* landing with the Ch4 tuning catalog (Push 4) | operation_catalog §Transpose | `reduce.rs` transpose arm | +| P12-K3 system-derived content rewrite | **decide: REFUSE (code follows)** — reduction MUST refuse intrinsic-content rewrites of `SYSTEM_DERIVED`-namespace pitches; appended `PreconditionFailureReason::SystemDerivedContentImmutable` (12). Core Ch5 states immutability; catalog pins the precondition for ModifyEvent + ModifyIdentifiedPitch | core_spec Ch5 §System-Derived Identifiers; operation_catalog §ModifyEvent; binary_format vocab (12) | protects Invariant 11; code tranche implements | +| P12-K4 ResolveConflict beyond concurrent | **decide: NO SUPERSEDE** — earliest-applied-resolve governs universally; causally-later differing resolves + any resolve against `Dismissed` read `AlreadyApplied`; re-resolution = future `ReopenConflict`-class op; no `TypedObjectId` Conflict kind (meta-conflict names both resolvers) | operation_catalog §ResolveConflict | `reduce.rs` resolve arm (as implemented) | +| P12-K5 equivocation selection policy | **defer (named site)** — v1 profiles declare *no* selection function (now stated); the hook's definition belongs to the Profile Conformance companion; no reducer policy hook until then | operation_catalog §ResolveEquivocation rationale | deliberate absence in `reduce.rs` | +| P12-K6 equivocation edge semantics | **adopt** — single-pass promotion (no fixpoint); quarantined resolves never govern (verified in `reduce.rs` pre-pass comment + code); pending-by-causal-gaps resolves still govern (set-level); invalid-target/chosen no-op keeps `TargetMissing` (dedicated reason rejected — verdict does not change caller behavior) | operation_catalog §ResolveEquivocation (new Edge semantics block) | `reduce.rs` promotion pre-pass | +| P12-K8 create score/canvas | **decide: GENESIS OUTSIDE OPS** — root + canvas are structural givens; genesis = empty-document constructor + bundle creation, normative; K1 slots *retired* (no kind will be assigned); revisit only under an addressable multi-canvas major | core_spec Ch5 §The Canvas; operation_catalog K1 chapter + Conformance Profiles | `Score::empty` + bundle creation path | +| P12-K9 differing-value re-creates | **decide: DEDICATED REASON (code follows)** — appended `PreconditionFailureReason::RecreateContentMismatch` (13) replaces the misnaming `TargetMissing` reuse at every differing-value re-create site (CreateStaff, carried TimeSignature, container creates) | operation_catalog §CreateStaff + §Meter and Tempo Overwrites; binary_format vocab (13) | code tranche implements | +| P12-K10 undo strand-blocks | **adopt (bless reuse)** — a StrictInverse strand-block *is* a transaction-scoped conflict; `TransactionConflict` reuse ratified; detail lives in the conflict record | operation_catalog §UndoTransaction | `undo.rs` | +| P12-K11 undo idempotence asymmetry | **adopt** — restorations are ordinary chain writes (no distinguished undo provenance); second-undo conflict + idempotent absence-restores are normative; revisit only under undo-as-operation | operation_catalog §UndoTransaction | `undo.rs` write chains | +| P12-K12 cross-region slur governance | **decide: AND** — boundary permeable only when both endpoint regions permit; advisory-only, never byte-affecting | core_spec Ch5 (after `Region` listing); operation_catalog §CreateCrossCutting | `validate.rs` conservative AND (as implemented) | +| P12-C1 multi-source cue | **adopt** — cascade on *any* source deletion is normative; rationale prose fixed to match ("losing any source breaks quotation integrity"); truncate-while-any-survives recorded as rejected | core_spec §Re-Anchoring Rule Table (Cue row) | `reduce.rs` cue cascade | +| P12-C2 Range truncate | **adopt** — truncate = dead event-anchored endpoint moves to its containing region's edge (start→Start, end→End, zero offset) | core_spec §Re-Anchoring Rule Table (graphic-gesture row) | `reduce.rs` re-anchor ledger | +| P12-C3 annotation orphaning | **adopt** — orphaning is the sanctioned outcome for wall-clock/indeterminate spans inexpressible as stored `Range` anchors; an expressible form is future model work | core_spec §Re-Anchoring Rule Table (annotation row) | `reduce.rs` | +| P12-C4 same-canvas reason | **decide: APPEND (code follows)** — `ReanchorReason::SameCanvasNearer` appended at discriminant **6** (5 was already owned by `DeclaredByExtension`); rank-4 survivors record it instead of `ExplicitFallback` | core_spec `ReanchorReason` listing + note; binary_format vocab (6) | code tranche implements | +| P12-E4 barrier matching | **adopt** — target-free ops (`SetMetadata`, `DeclareTransaction`) match score-wide barriers only; opaque `Registered` ops match fully conservatively (`req:format:barrier-matching`) | core_spec Ch8 | `editor-core` barrier gate | +| P12-E5 unsafe-edit tombstone | **adopt semantics + defer encoding (named site)** — `req:format:unsafe-tombstone`: immediate deactivation of the crossed extension's remaining barriers; durable record MUST land at next commit; `required = true` → read-only for dependents. The manifest-side *byte encoding* is a new binary_format open question (manifest frozen at major 0 → blob-layer or new chunk kind; next bundle-format tranche) | core_spec Ch8; binary_format §extension blobs (open question) | `editor-core::extensions_requiring_tombstone()` (producer exists) | +| P12-I4 constraint strength | **adopt** — strength is *kind-determined*, no instance field (`req:solver:kind-strength`): breaks by `BreakKind` (Hard→Required, Soft→Preferred{1.0}), core families Required, `Registered` conservative Required; future families declare strength in their definitions. Deliberate Standard-tier design input | core_spec Ch9 §Strength Levels | `layout-ir` normalization | +| P12-I5 sub-conformant report | **adopt** — `SolvedWithWarnings` + `satisfied_hard_constraints == false` + warning is the sanctioned constraints-present-but-unevaluated report; the one renderable status with unsatisfied-hard, because the field reports *evaluated* satisfaction (`req:solver:subconformant-report`) | core_spec Ch9 §SolveReport | `layout-ir` stub solver | +| P12-I6 Minimal constraint floor | **adopt** — the implemented emission set (successive-notehead no-collision chains + per-glyph containment + user-break constraints) is the normative Minimal floor (`req:layoutir:constraint-floor`); higher-tier floors defined when those tiers land | core_spec Ch7 §ConstrainedLayoutIR | `constrained.rs` emission | + +**Ride-along staleness fix:** the Ch8 `OperationKindTag` listing gained the +eleven appended tags the code has carried since M2/Phase-3 (append-only +vocabulary; the listing had drifted). + +**Version movements.** Operation Catalog 0.5.0 → 0.6.0. Binary Format +0.3.0 → 0.4.0 (vocab appends 12/13/6 + the E5 encoding open question). Core +spec: revision-history row "Pass 12 G-ratification (the batch pass)"; two +open-question boxes replaced by ratified requirements (spelling, +decomposition), one narrowed (Ch4 spelling-catalog), one added +(binary_format E5 encoding). All three PDFs rebuilt clean, zero undefined +references. + +**Code tranche (follows this commit):** H7 authored-only surfacing + taxonomy +buckets (`prepass.rs`); K3 `SystemDerivedContentImmutable` (12); K9 +`RecreateContentMismatch` (13); C4 `SameCanvasNearer` (6) — each with +regression tests and wire goldens. diff --git a/spec/PASS12_WORKLIST.md b/spec/PASS12_WORKLIST.md new file mode 100644 index 0000000..bfcf845 --- /dev/null +++ b/spec/PASS12_WORKLIST.md @@ -0,0 +1,297 @@ +# Pass 12 — G-Ratification Worklist (the batch pass) + +*Purpose: retire the accumulated `PASS12_BATCH.md` backlog — 28 open rows spanning +six agents — in one deliberate G pass, before the data-model schema-major-2 push +starts consuming graph-model decisions (H2, K8) and the Standard-tier solver push +starts consuming solver-contract decisions (I4/I5/I6).* + +*Scope: a **spec revision pass** plus a small, explicitly-listed code tranche. +The architecture stays frozen. Most rows bless an implemented, DECISIONS-recorded +reading; four rows carried genuine forks and were decided by the project lead on +2026-07-07 (see "Key decisions" below); three rows defer to named future tracks +with rationale.* + +*Working rule (Pass-11 precedent): **adopt** = bless the implementation's existing +choice in normative spec text. **decide** = a real fork, resolved and recorded +here. **defer** = punt to a *named* landing site with rationale — never a silent +drop. Every disposition lands in `PASS12_RATIFICATION_LOG.md`; every retired row +is struck through in `PASS12_BATCH.md` citing its disposition.* + +--- + +## Key decisions this pass (project lead, 2026-07-07) + +| Row | Decision | +|---|---| +| **P12-K12** | Cross-region slur spanning permission = **AND** (both endpoint regions must permit). As implemented; advisory-only, never byte-affecting. | +| **P12-H7** | Authored spelling/decomposition attachments on inference-ineligible events **surface in derived annotations** (authored-only resolution path + new taxonomy buckets). Code work in both pre-passes. | +| **P12-K4** | ResolveConflict: **no supersede** — earliest applied resolve governs universally (concurrent *and* causally-later); later resolves and resolves against `Dismissed` read `AlreadyApplied`. Re-resolution is a future dedicated operation. No `TypedObjectId::Conflict` kind; the meta-conflict names both resolver op ids, as built. | +| **P12-K8** | **Genesis outside the operation set** — the document root and canvas are structural givens (`Score::empty` + bundle creation), never op-minted. The K1 create-score/create-canvas slots are retired as *deliberately outside the operation set*, not "unavailable". Revisit only if multi-canvas becomes a real major-2+ feature. | + +--- + +## Bucket 1 — Adopt-and-pin (spec text only; 19 entries / 20 rows) + +No code changes; each blesses a deterministic implemented reading. Golden/test +anchors already exist for the byte-adjacent ones. + +### 1.1 — P12-H1 — `SpellingAlgorithmId::Default` ratified +- **Spec locus:** core_spec Ch 2 (spelling pre-pass) + Appendix D §Open Algorithm Hooks. +- **Ratify:** `"default"` = the Temperley-style line-of-fifths preference algorithm, + v1 (`epiphany-core/src/prepass.rs`). The id is now the *spec's*, not the crate's + proposal. `CONFORMANCE.md` already declares it; drop its "pending ratification" caveat. + +### 1.2 — P12-H3 — chromatic-run convention is algorithm-defined +- **Spec locus:** core_spec Ch 2, same section as 1.1. +- **Ratify:** enharmonic choice in the absence of tonal context is a property of the + *versioned* spelling algorithm, not spec-pinned; v1's centre-of-gravity rule with + the ascending-sharps/descending-flats convention **as tiebreak only** is the + ratified v1 behavior. A voice-leading refinement = a future algorithm version, not + a spec hole. + +### 1.3 — P12-H4 (+ P12-C5 folded) — decomposition v1 scope bounds +- **Spec locus:** core_spec Ch 3 (decomposition pre-pass) + Appendix D hooks; + cross-ref operation_catalog §SetTimeSignature for C5. +- **Ratify:** the v1 `DecompositionAlgorithmId "default"` bounds become *declared + normative bounds of the versioned algorithm*: single governing meter per region; + region origin assumed a barline (no anacrusis); dyadic compound-meter grouping; + no tuplet nesting / cross-beat members; `MAX_DOTS = 1`. A wider algorithm is a + version bump that deterministically invalidates derived output (the derived- + annotation model already guarantees this). +- **C5 disposition:** reduction semantics of a mid-region `SetTimeSignature` are + already pinned (catalog §Meter and Tempo Overwrites); the derived-notation gap is + *subsumed by the H4 single-meter bound* — one log entry covers both rows. + +### 1.4 — P12-H5 — aleatoric spelling open question closed +- **Spec locus:** core_spec Ch 2 open-question box (aleatoric regions). +- **Ratify:** spelling is region-time-model-**independent** (pitch identity does not + depend on the time model); there is no aleatoric-specific spelling pass. Close the + open question with that answer; aleatoric-aware refinements are algorithm-version + territory. + +### 1.5 — P12-H6 — decomposition precedence is FIXED (decided: not configurable) +- **Spec locus:** core_spec Ch 3 §decomposition precedence. +- **Ratify:** decomposition precedence is the fixed default source order + (`UserChosen > Imported > Propagated > Inferred`, canonical attachment order as + tie-break) — **not** configurable. Rationale: configurability requires a new + canonical `Score` field (a schema-major) with no consumer; `DecompositionAttachment` + deliberately carries no `priority`. Revisit at a future major only if a use case + appears. The "same precedence machinery as spelling" sentence is reworded to "same + source-rank discipline" (spelling's *configurability* is spelling-specific). + +### 1.6 — P12-K1 — v0 RespellPitch migration fallback +- **Spec locus:** operation_catalog §RespellPitch, §Migration. +- **Ratify:** context-recovery (explicit per-pitch attachment whose canonical bytes + hash to the fingerprint) + `MigrationError::Irreversible` → bundle read-only when + absent is the **long-term** disposition. No richer v0 corpus will be required + (there is no production v0 corpus). + +### 1.7 — P12-K4 — ResolveConflict beyond the concurrent case *(decided above)* +- **Spec locus:** operation_catalog §ResolveConflict; core_spec Ch 6 §Conflict Records. +- **Ratify:** earliest-applied-resolve-governs applies to causally-later resolves + (they read `AlreadyApplied`); resolves against `Dismissed` read `AlreadyApplied`; + intentional re-resolution is out of the v1 operation set (a future `ReopenConflict`- + class op is the sanctioned path). Conflict records get **no** addressable + `TypedObjectId` kind; the meta-conflict names both resolver operation ids. + +### 1.8 — P12-K6 — ResolveEquivocation edge semantics +- **Spec locus:** operation_catalog §ResolveEquivocation. +- **Ratify as implemented:** single-pass promotion (a promoted candidate that is + itself a resolve does not govern a further promotion — no fixpoint); the set-level + rule (a resolve held pending by its own causal gaps still *governs promotion* while + its own effect stays pending); invalid-target/chosen no-ops reuse `TargetMissing` + (consistent with the K9 disposition below, which scopes its new reason to + differing-value *re-creates* only). **Execution note:** read + `epiphany-ops` and write down the implemented quarantine interaction verbatim + (may a quarantined resolve govern?) — the ratification text records what the code + does; if the code turns out to have no defined behavior there, the text says + "quarantined resolves are excluded from governing" only if a test proves it. + +### 1.9 — P12-K8 — genesis outside the operation set *(decided above)* +- **Spec locus:** operation_catalog K1 chapter (retire the two slots) + core_spec + Ch 5 (root/canvas genesis note) + Ch 6 (operation-set completeness statement). + +### 1.10 — P12-K10 — undo strand-blocks keep `TransactionConflict` +- **Spec locus:** operation_catalog §UndoTransaction. +- **Ratify:** a StrictInverse strand-block (refusing to tombstone a minted object + still referenced by a live non-member) **is** a transaction-scoped conflict; + `ConflictKind::TransactionConflict` reuse is blessed — no new conflict kind. The + refusal detail lives in the conflict record's affected-objects/description, as built. + +### 1.11 — P12-K11 — undo idempotence asymmetry ratified +- **Spec locus:** operation_catalog §UndoTransaction. +- **Ratify:** an undo's value restorations are ordinary chain writes by the undo op + (no distinguished provenance); a second undo of the same transaction sees the first + as superseding (`Conflicted` under StrictInverse, skipped under BestEffort) while + absence-restorations repeat idempotently. The asymmetry is documented normative + behavior. Revisit only under the deferred undo-as-operation (streaming-consistent + undo) track, which subsumes it. + +### 1.12 — P12-K12 — cross-region slur permission = AND *(decided above)* +- **Spec locus:** core_spec validation/advisory section that defines the + `permits_spanning_slurs` advisory (schema-major-1 Phase A text) + catalog + §CreateCrossCutting advisory note. +- **Ratify:** a region boundary is permeable to a spanning slur only when **both** + endpoint regions permit. Advisory-only; never alters reduction. + +### 1.13 — P12-C1 — multi-source cue re-anchoring = cascade +- **Spec locus:** core_spec §Re-Anchoring Rule Table (Cue row rationale). +- **Ratify:** the table's action column is normative as written — **any** source + deletion cascade-deletes the cue (like Tie). Fix the rationale prose to match + ("a cue that loses any source loses its quotation integrity"); truncate-while-any- + survives is a rejected alternative, recorded in the log. + +### 1.14 — P12-C2 — graphic-gesture Range "truncate" defined +- **Spec locus:** core_spec §Re-Anchoring Rule Table (graphic gesture row). +- **Ratify:** a dead event-anchored range endpoint moves to its containing region's + edge — start endpoint → region Start, end endpoint → region End, zero offset. As + implemented. + +### 1.15 — P12-C3 — inexpressible reconstructed ranges orphan +- **Spec locus:** core_spec §Re-Anchoring Rule Table (analytical annotation row). +- **Ratify:** a wall-clock (region-relative) or indeterminate event span that cannot + be expressed as a stored `Range` anchor **orphans** (recorded as such); an + expressible form is future model work, not required. + +### 1.16 — P12-E4 — conservative barrier matching +- **Spec locus:** core_spec Ch 8 (edit barriers / extension declarations). +- **Ratify:** operations with no graph target (`SetMetadata`, `DeclareTransaction`) + are matched by **score-wide barriers only**; opaque `Registered` operations match + **fully conservatively**. As implemented in editor-core's barrier gate. + +### 1.17 — P12-I4 — constraint strength is kind-determined +- **Spec locus:** core_spec Ch 9 (normalized constraint form) + Ch 7 `LayoutConstraint`. +- **Ratify:** no per-instance strength field. Strength is determined by constraint + kind: break constraints by `BreakKind` (Hard → Required, Soft → Preferred{1.0}); + the other core families Required; `Registered` conservatively Required. Future + (Standard-tier) constraint families **declare their strength in their normative + definition** — the channel is the kind, not the instance. This is a deliberate + Push-3 design input. + +### 1.18 — P12-I5 — sub-conformant solver report shape +- **Spec locus:** core_spec Ch 9 (SolveStatus semantics; next to the Pass-12-tranche-1 + `SolverTier::Stub` text). +- **Ratify:** a below-conformance passthrough solver reports constraints-present-but- + not-evaluated as `SolvedWithWarnings` + `satisfied_hard_constraints == false` + a + dedicated warning. Renderable, honest, makes no conformance claim. + +### 1.19 — P12-I6 — the Minimal-tier constraint-emission floor +- **Spec locus:** core_spec Ch 9 (spacing-pass requirements). +- **Ratify:** the normative Minimal floor = successive-notehead-column no-collision + chains + per-glyph region containment + user-break constraints, exactly the + implemented emission set. Standard's floor is defined when the Standard tier lands + (Push 3). Makes the Minimal acceptance surface testable. + +--- + +## Bucket 2 — Decide-then-build (spec text + code; 5 rows) + +Spec-first discipline: the normative text lands in the same pass, the code follows +in a separate commit, each change regression-tested. + +### 2.1 — P12-H7 — surface authored annotations for ineligible events *(decided above)* +- **Spec:** core_spec Ch 2 + Ch 3 pre-pass sections — derived annotations include, for + events/pitches the algorithm produces no output for, the winning **authored** + attachment (same source-rank discipline); taxonomy counts them in dedicated + authored-only buckets. +- **Code:** `epiphany-core/src/prepass.rs` — authored-only resolution path in both + `resolve_spelling`-adjacent and `resolve_decomposition` surfaces; new + `TaxonomyReport` buckets (e.g. `spellings_authored_uninferred`, + `decompositions_authored_uninferred`), serialized into the derivation fingerprint + (derived annotations only — **no canonical-byte impact**; the fingerprint is not + canonical state). Regression tests: an authored attachment on an ungriddable event + surfaces; an outranked one does not; fingerprint changes deterministically. + +### 2.2 — P12-K3 — refuse content rewrites of SYSTEM_DERIVED pitches +- **Spec:** operation_catalog §ModifyEvent + §Identified-Pitch Operations gain the + precondition; core_spec Ch 5 Invariant 11 rationale cross-ref. +- **Code:** reduction-time precondition — a `ModifyEvent`/`ModifyIdentifiedPitch` + that would rewrite the *intrinsic content* of a `SYSTEM_DERIVED` pitch refuses as a + clean no-op with a **new appended** `PreconditionFailureReason` (proposed: + `SystemDerivedContentImmutable`). Append-only vocabulary = sanctioned minor + evolution (precedent: `TempoMapMalformed = 11`). Tests: direct refusal, reduce == + reduce_onto agreement, wire golden for the new discriminant. + +### 2.3 — P12-K9 — dedicated reason for differing-value re-creates +- **Spec:** operation_catalog (CreateStaff, carried TimeSignature, container creates). +- **Code:** appended `PreconditionFailureReason` (proposed: `RecreateContentMismatch`) + replacing the misnamed `TargetMissing` on live-id-re-carried-with-different-content + refusals. Same test discipline as 2.2. + +### 2.4 — P12-C4 — appended `ReanchorReason::SameCanvasNearer` +- **Spec:** core_spec §Re-Anchoring Rule Table (rank-4 survivor recording). +- **Code:** append the discriminant (after `ExplicitFallback = 4`; before + `DeclaredByExtension`'s registered space — confirm the discriminant table allows a + clean append; if `DeclaredByExtension` already owns 5, take 6 and record why). + Rank-4 (same-canvas) survivors record it instead of `ExplicitFallback`. Wire golden + + one re-anchoring test updated. + +### 2.5 — P12-E5 — unsafe-edit tombstone: semantics now, encoding deferred +- **Spec (this pass):** core_spec Ch 8 ratifies the *semantics*: crossing a barrier + via `apply_unsafe` immediately deactivates the extension's remaining barriers for + the session; the crossing MUST be durably recorded at the next bundle commit; a + tombstoned `required = true` extension makes the bundle open **read-only** for + writers that honor the extension (they can no longer trust its invariants). +- **Deferred (named site):** the manifest-side *encoding* of the tombstone record — + the manifest is major-0-forever (schema-major-1 design decision), so the record + must ride the extension-declaration blob layer or a new chunk kind; that design + belongs to the next bundle-format tranche. Add an `openquestion` box to + `binary_format.tex` naming the constraint. Editor-core's + `extensions_requiring_tombstone()` is the implemented producer awaiting that + consumer. + +--- + +## Bucket 3 — Defer-with-rationale (3 rows, named landing sites) + +### 3.1 — P12-H2 — key/clef model: premise stale, remainder deferred +- **Finding (verified 2026-07-07):** the row's premise is stale — the content model + *exists* (I-0: `Clef`, `KeySignature`, content-bearing `ClefChange`/ + `KeySignatureChange`) and layout consumes it (`PlacedKeySignature`, `active_clef`). + What remains is algorithmic: the spelling pre-pass never consults declared keys + (0 references in `prepass.rs`), and notation does no key-aware accidental + suppression / cancelling naturals. +- **Defer:** key-aware spelling → a **spelling algorithm v2** (versioned-algorithm + rev, invalidates derived output deterministically — no spec hole); key-aware + accidental display + cancelling naturals → the **notation/engraving refinement + backlog** (major-2 / Standard-tier neighborhood). Batch row struck with the + narrowed statement. + +### 3.2 — P12-K2 — Transpose interval algebra → Chapter-4 tuning track +- **Defer:** the faithful interval representation (diatonic/chromatic, octave/nominal + renormalization, non-CMN pitch spaces) lands with the tuning catalog (Push 4). + **Pin now (one sentence each):** the v1 payload's `chromatic_steps: i32` semantics + = CMN alteration shift with documented `i8` saturation — a declared prototype whose + replacement is a *payload schema-major* under the companion's evolution rule; and + Transpose stays excluded from undo inversion (already documented). + +### 3.3 — P12-K5 — profile-declared equivocation selection → profile track +- **Defer:** the third resolution path (profile-declared deterministic selection + function) stays unpinned; v1 profiles declare **none** (ratify that sentence in the + catalog §ResolveEquivocation rationale). The hook's definition belongs to the + Profile Conformance companion, which does not exist yet — named landing site. + +--- + +## Execution plan + +Order of work (each tranche gated; spec-first before code): + +1. **Tranche A — core_spec pre-pass text** (H1, H3, H4+C5, H5, H6, H7-normative, + H2-narrowing): Ch 2/Ch 3 edits, two open-question boxes closed. +2. **Tranche B — operation_catalog text** (K1, K4, K6, K8, K10, K11, K12, K2-pin, + K5-pin, K3/K9-normative): catalog version bump 0.5.0 → 0.6.0. +3. **Tranche C — core_spec re-anchoring + barriers + solver** (C1, C2, C3, C4-normative, + E4, E5-semantics, I4, I5, I6) + binary_format openquestion (E5 encoding). +4. **Both PDFs rebuild clean** (lualatex core_spec; xelatex catalog; check + binary_format too), zero undefined refs. → **Commit 1 (spec).** +5. **Tranche D — code** (H7 surfacing + taxonomy; K3 + K9 reasons; C4 variant), full + gate (fmt, clippy -D warnings, workspace tests, conformance scale 1), review pass, + → **Commit 2 (code).** +6. **Process trail:** PASS12_RATIFICATION_LOG "G-pass tranche" section (dispositions + table, version movements, key decisions); PASS12_BATCH rows struck (28 → 0 open); + crate DECISIONS cross-refs (core, ops, layout-ir, editor-core); CONFORMANCE.md + H1 caveat dropped. Rides Commit 1/2 as appropriate. + +After this pass the batch is **empty** and Push 2 (data-model major 2) starts with +no pending graph-model questions: H2 narrowed, K8 decided, H6 decided. diff --git a/spec/binary_format.pdf b/spec/binary_format.pdf index b032b39..d2d2a49 100644 Binary files a/spec/binary_format.pdf and b/spec/binary_format.pdf differ diff --git a/spec/binary_format.tex b/spec/binary_format.tex index 7edfd2c..a12990f 100644 --- a/spec/binary_format.tex +++ b/spec/binary_format.tex @@ -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.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] + {\normalsize\color{epiphanyink}Version 0.4.0 --- Pass-12 G-ratification (appended effect-vocabulary discriminants; unsafe-edit tombstone encoding open question)}\\[4pt] {\small\color{epiphanyslate}Normative for the byte layouts it defines} \vfill \end{titlepage} @@ -1562,7 +1562,9 @@ trailing bytes are decode errors. \tablenums{8} ExtensionPrecondition \cat{} id (16 BE); \tablenums{9} Registered \cat{} id (16 BE); \tablenums{10} ContainerNotEmpty; - \tablenums{11} TempoMapMalformed. \\ + \tablenums{11} TempoMapMalformed; + \tablenums{12} SystemDerivedContentImmutable (Pass~12, P12-K3); + \tablenums{13} RecreateContentMismatch (Pass~12, P12-K9). \\ \texttt{RepairRecord} & (struct) \texttt{kind} (\texttt{RepairKind}) \cat{} \texttt{target} (\texttt{TypedObjectId}). \\ @@ -1581,7 +1583,10 @@ trailing bytes are decode errors. \tablenums{0} SameVoiceNearer; \tablenums{1} SameStaffInstanceNearer; \tablenums{2} SameStaffNearer; \tablenums{3} SameRegionNearer; \tablenums{4} ExplicitFallback; - \tablenums{5} DeclaredByExtension \cat{} id (16 BE). \\ + \tablenums{5} DeclaredByExtension \cat{} id (16 BE); + \tablenums{6} SameCanvasNearer (Pass~12, P12-C4: semantically the + rank-4 proximity variant; appended at 6 because 5 was already + owned by DeclaredByExtension). \\ \texttt{TupletCompensationKind} & \tablenums{0} ReplaceWithRest; \tablenums{1} RewriteTuplets; \tablenums{2} CascadeDeleteTuplets. (No payload; distinct from the @@ -2095,6 +2100,20 @@ are carried \emph{opaquely inside the canonical manifest} must continue to parse them as \texttt{u64}. Their unification waits for a future major that revises the manifest (Section~\ref{sec:evolution:major1}). +\begin{openquestion} +\textbf{Unsafe-edit tombstone encoding (from P12-E5).} Core Chapter~8 now +ratifies the unsafe-edit tombstone \emph{semantics} (immediate deactivation of +the crossed extension's remaining barriers; a durable record \MUST{} land at +the next commit; \texttt{required = true} interaction). The manifest-side +\emph{byte encoding} of that record is unresolved --- and constrained: the +manifest schema is frozen at major~0, so the record cannot be a new manifest +field. Candidate channels: a marker inside the extension-declaration blob +layer (this chapter's regime-(b) surface), or a dedicated chunk kind +referenced from \texttt{preserved\_chunk\_roots}. To be designed in the next +bundle-format tranche; the editor-side producer +(\texttt{extensions\_requiring\_tombstone()}) already exists. +\end{openquestion} + \section{Framing} \label{sec:barriers:framing} @@ -2775,6 +2794,15 @@ layouts of Section~\ref{sec:values:representative}. ops read-only), and the total default-filling v0${\to}$v1 migration table. Clarifies that any field add is a major change regardless of \texttt{Option}-ness. \\ + \today & Effect vocabularies / Extension blobs & 0.4.0 --- Pass-12 + G-ratification: appends \texttt{PreconditionFailureReason} + \tablenums{12} (\texttt{SystemDerivedContentImmutable}, P12-K3) and + \tablenums{13} (\texttt{RecreateContentMismatch}, P12-K9), and + \texttt{ReanchorReason} \tablenums{6} (\texttt{SameCanvasNearer}, + P12-C4) --- schema-\emph{minor} appends under Chapter~\ref{ch:evolution}; + no existing assignment changed. Adds the unsafe-edit tombstone + \emph{encoding} open question (from P12-E5; semantics ratified in core + Chapter~8). Semantics: Operation Catalog 0.6.0. \\ \bottomrule \end{longtable} diff --git a/spec/core_spec.pdf b/spec/core_spec.pdf index eda38be..63874f8 100644 Binary files a/spec/core_spec.pdf and b/spec/core_spec.pdf differ diff --git a/spec/core_spec.tex b/spec/core_spec.tex index a813422..34aef9f 100644 --- a/spec/core_spec.tex +++ b/spec/core_spec.tex @@ -406,18 +406,22 @@ Section~\ref{sec:det:open}: the document have not yet been delivered: Appendix~\ref{app:deferred} catalogs them and distinguishes them from accidentally missing content. - \item Several algorithms affect canonical state and have not + \item Two algorithms affect canonical state and have not yet received one of the dispositions in Appendix~\ref{app:determinism} - Section~\ref{sec:det:open}: the spelling pre-pass, the - notational-decomposition algorithm, the tempo curve - integration algorithm, and the + Section~\ref{sec:det:open}: the tempo curve + integration algorithm and the \texttt{wallclock\_to\_musical} root-finding algorithm. Cross-implementation byte equality of canonical score state \MUSTNOT{} be claimed for outputs derived from these algorithms until they are normatively specified or profile-declared (Appendix~\ref{app:determinism} - Section~\ref{sec:det:layers}). + Section~\ref{sec:det:layers}). The spelling pre-pass and the + notational-decomposition algorithm received the + profile-declared-by-versioned-identifier disposition in + Pass~12 + (Requirements~\ref{req:pitch:spelling-algorithm} + and~\ref{req:time:decomposition-algorithm}). \item Extension registry catalogs are referenced by typed identifier but not enumerated here; they are delivered as separate, versioned registry documents. @@ -1322,7 +1326,9 @@ The pre-pass operates as follows: \item Collect all pitches in the score, partitioned by voice and measure. \item For each partition, in time order, resolve each pitch's spelling - using: + using the applicable context inputs (which of these a given + algorithm version consumes is part of its versioned definition; + see Requirement~\ref{req:pitch:spelling-algorithm}): \begin{enumerate} \item The active key signature for the staff. \item The accidental context of prior pitches in the same measure @@ -1357,20 +1363,53 @@ annotations produced. operation, which then outranks the pre-pass. \end{rationale} -\begin{openquestion} - The melodic and harmonic context rules above are stated - abstractly. The specific algorithm---whether based on - Longuet-Higgins line-of-fifths distance, Temperley's - preference rules, or another approach---is unresolved. The - choice affects canonical state and must therefore receive one - of the dispositions in Appendix~\ref{app:determinism} - Section~\ref{sec:det:open}: delivery as a normative algorithm - in a Spelling Pre-Pass companion specification, - profile-declared by versioned identifier, or explicitly marked - non-canonical (in which case automatic spellings are advisory - and \texttt{UserChosen} attachments are the only canonical - spellings). -\end{openquestion} +\begin{requirement} + \label{req:pitch:spelling-algorithm} + \textbf{Spelling algorithm disposition (ratified Pass 12).} + The spelling pre-pass receives the \emph{profile-declared by + versioned identifier} disposition of Appendix~\ref{app:determinism} + Section~\ref{sec:det:open}. The reserved identifier + \texttt{SpellingAlgorithmId} \texttt{"default"} denotes, at + version~1, a Temperley-style line-of-fifths preference + algorithm: each partition's tonal context is estimated as a + centre of gravity on the line of fifths from the accidental, + melodic, and harmonic context, and each pitch takes the + enharmonic spelling nearest that centre. The ascending-runs-take- + sharps / descending-runs-take-flats convention participates + \emph{only as a tiebreak} within the centre-of-gravity rule; an + isolated chromatic run with no tonal context may therefore + receive the enharmonic the convention alone would not pick, and + a voice-leading refinement is a future algorithm version, not a + deviation. Which of the context inputs enumerated above an + algorithm version consumes is part of its versioned definition: + \texttt{"default"} version~1 does \emph{not} consult declared + key signatures (a key-aware refinement is a future version) and + is region-time-model-independent --- pitches in aleatoric and + proportional regions are spelled by the same rule as metric + ones, and no region-specific spelling pass exists. A profile + requesting any other identifier \MUST{} error; + implementations \MUSTNOT{} silently substitute a different + algorithm. +\end{requirement} + +\begin{requirement} + \label{req:pitch:authored-uninferred} + \textbf{Authored annotations for inference-ineligible targets + (ratified Pass 12).} + A stored spelling attachment whose target the pre-pass produces + \emph{no} inferred spelling for (an inference-ineligible pitch) + \MUST{} still surface in the resolved derived annotations: the + winning attachment under the precedence configuration + (Section~\ref{sec:pitch:precedence}) is reported with its + authored provenance, exactly as it would have been had it + outranked an inferred spelling. An authored attachment is + precisely how a user notates what the algorithm cannot infer; + the derived-annotation surface \MUSTNOT{} render it invisible. + Annotation taxonomy counts these authored-only resolutions + distinctly from authored-over-inferred overrides. The same rule + applies to the decomposition pre-pass + (Section~\ref{sec:time:notrhythm}). +\end{requirement} \subsection{Absent Accidentals} \label{sec:pitch:absent-accidental} @@ -2188,15 +2227,32 @@ pub enum DecompositionSource { \end{lstlisting} The structure deliberately mirrors the spelling attachment model -(Section~\ref{sec:pitch:spelling}): same sources, same precedence -machinery, same pre-pass discipline. +(Section~\ref{sec:pitch:spelling}): same sources, same source-rank +discipline, same pre-pass output model. Unlike spelling, decomposition +precedence is \emph{not configurable} (ratified Pass~12): the fixed +default source order \texttt{UserChosen} $>$ \texttt{Imported} $>$ +\texttt{Propagated} $>$ \texttt{Inferred} applies, with the score's +canonical attachment order as tie-break among equal-rank authored +attachments; \texttt{DecompositionAttachment} carries no +\texttt{priority} field and the graph carries no +\texttt{DecompositionPrecedence} configuration. Making precedence +configurable would add a canonical \texttt{Score} field (a +schema-major change under the Binary Format companion's evolution +rule) for which no consumer exists; the decision may be revisited at +a future schema major if a use case appears. \subsection{The Decomposition Pre-Pass} When a score is materialized, the decomposition pre-pass computes inferred decompositions, reported as derived annotations; an authored higher-precedence attachment overrides the inferred value in the -resolved annotation. The algorithm is, in outline: +resolved annotation. An authored attachment targeting an event the +pre-pass produces \emph{no} inferred decomposition for (ungriddable, +non-metric, or an inapplicable event kind) surfaces as the resolved +annotation on its own, per +Requirement~\ref{req:pitch:authored-uninferred} --- authoring is how +a user notates exactly what the algorithm cannot infer. The +algorithm is, in outline: \begin{enumerate} \item For each event in time order within a voice and measure, @@ -2224,22 +2280,43 @@ resolved annotation. The algorithm is, in outline: annotations produced. \end{requirement} -\begin{openquestion} - The specific algorithm for selecting decompositions when multiple - are valid (e.g., $\frac{3}{8}$ in $\frac{4}{4}$ may be a dotted - quarter or a quarter tied to an eighth depending on placement) - involves style choices. The default rules must be specified; - user-configurable alternate rule sets are likely needed. The - notational-decomposition algorithm affects canonical state and - must therefore receive one of the dispositions in - Appendix~\ref{app:determinism} - Section~\ref{sec:det:open}: delivery as a normative algorithm - in the Notational Decomposition companion specification, or - profile-declared by versioned identifier. Until that disposition - is made, the default-decomposition output is non-canonical and - cross-implementation byte equality \MUSTNOT{} be claimed for - derived layouts. -\end{openquestion} +\begin{requirement} + \label{req:time:decomposition-algorithm} + \textbf{Decomposition algorithm disposition (ratified Pass 12).} + The decomposition pre-pass receives the \emph{profile-declared + by versioned identifier} disposition of + Appendix~\ref{app:determinism} Section~\ref{sec:det:open}. The + reserved identifier \texttt{DecompositionAlgorithmId} + \texttt{"default"} denotes, at version~1, the integer-grid + metric splitter, whose scope bounds are part of its normative + definition: + \begin{itemize} + \item a single governing meter per region --- a mid-region + meter change reduces cleanly into the region's grid (the + Operation Catalog pins those semantics), but derived + notation honours only the first governing meter until a + wider algorithm version lands; + \item the region origin is assumed to fall on a barline + (anacrusis/pickup handling deferred); + \item compound-meter beat grouping uses the dyadic default; + \item tuplet nesting and cross-beat tuplet members are out of + scope; + \item at most one augmentation dot + (\texttt{MAX\_DOTS}~$=$~1); a double-dotted value is + written as tied components --- correct, if not the most + compact. + \end{itemize} + A wider algorithm is a \emph{version bump}, which + deterministically invalidates derived output under the + derived-annotation model --- no state migration. Style-choice + selection among multiple valid decompositions (e.g., + $\frac{3}{8}$ in $\frac{4}{4}$ as a dotted quarter versus a + quarter tied to an eighth) is fixed by the versioned algorithm; + user-configurable alternate rule sets, if ever wanted, are new + registered identifiers. A profile requesting any identifier + other than a registered one \MUST{} error; implementations + \MUSTNOT{} silently substitute. +\end{requirement} \section{Tuplets as Grouping Objects} \label{sec:time:tuplets} @@ -2773,11 +2850,14 @@ pub struct SpellingRuleSet { \end{lstlisting} \begin{openquestion} - The catalog of registered spelling algorithms is the subject of the - open question parked in Section~\ref{sec:pitch:prepass}. Candidates - include Longuet-Higgins line-of-fifths distance, Temperley preference - rules, Cambouropoulos pitch-spelling, and Meredith PS13. The chosen - default and the parameter schemas are normative once decided. + The default spelling algorithm is now ratified + (Requirement~\ref{req:pitch:spelling-algorithm}: + \texttt{"default"} = Temperley-style line-of-fifths preference, + version~1). Still open: the catalog of \emph{additional} + registered spelling algorithms (candidates include + Longuet-Higgins line-of-fifths distance, Cambouropoulos + pitch-spelling, and Meredith PS13) and their parameter schemas, + which are normative once registered. \end{openquestion} \section{Accidental Registries} @@ -3642,6 +3722,15 @@ reserves a dedicated replica namespace. regenerate the random portion until a non-reserved value is obtained. + Because a system-derived identifier is content-derived, the + identified object's intrinsic content is \emph{immutable}: a + reduction \MUST{} refuse an operation that would rewrite the + intrinsic content of a \texttt{SYSTEM\_DERIVED}-namespace object + in place (ratified Pass~12; the Operation Catalog pins the + precondition and its failure reason). Rewriting the content would + silently invalidate the id's content derivation; the sanctioned + path is minting a replacement object. + System-derived identifiers within the \texttt{ReplicaId::SYSTEM\_DERIVED} namespace \MUST{} have their 64-bit counter portion derived deterministically by BLAKE3 @@ -4239,6 +4328,17 @@ pub struct CueEvent { The canvas is the spatial root of the score. It contains regions. +The score root and the canvas are \emph{structural givens}, not +operation products (ratified Pass~12): genesis is the creation of an +empty score together with its bundle, outside the operation set, and +neither object is ever minted, addressed, or deleted by an operation +(there is no \texttt{TypedObjectId} kind for either). This is +deliberate --- a document that could mint its own root would admit a +genesis race under concurrent editing, and every operation would need +a defined semantics against a not-yet-existing root. The decision may +be revisited only if an addressable multi-canvas model is adopted at +a future schema major. + \begin{lstlisting}[language=Rust] pub struct Canvas { /// Regions in this canvas. Each region declares its time and @@ -4314,6 +4414,17 @@ pub struct Region { /// schema major 1. pub permits_spanning_slurs: bool, } +\end{lstlisting} + +For a slur (or other spanner) whose endpoints lie in \emph{different} +regions, permission is governed conjunctively (ratified Pass~12): +the boundary is permeable only when \emph{both} endpoint regions set +\texttt{permits\_spanning\_slurs} --- a region that forbids spanning +is never crossed against its declaration, regardless of which side +the spanner starts on. The check is an authoring-time advisory only; +it never alters canonical reduction. + +\begin{lstlisting}[language=Rust] pub enum RegionContent { /// Staff-based notation: one or more staves carrying voices and @@ -6926,9 +7037,18 @@ pub enum ReanchorReason { SameRegionNearer, ExplicitFallback, DeclaredByExtension(ReanchorReasonRegistryId), + /// A rank-4 (same-canvas) proximity survivor. Appended in + /// Pass 12 (P12-C4); its wire discriminant is 6 because + /// DeclaredByExtension already owned 5 when it was appended. + SameCanvasNearer, } \end{lstlisting} +A re-anchor that selects a rank-4 (same-canvas) survivor records +\texttt{SameCanvasNearer} (ratified Pass~12); before the variant was +appended, such repairs were recorded \texttt{ExplicitFallback}, a +recording this revision supersedes. + \subsection{Total Ordering for "Nearest"} The notion of ``nearest surviving anchor'' is defined as a total @@ -7022,13 +7142,19 @@ table is normative. User content never silently deleted. \\ Cue event & Source event & - Cascade-delete & - A cue with no source is meaningless. \\ + Cascade-delete on any source deletion & + a multi-source cue cascades when \emph{any} source dies + (ratified Pass~12): losing any source breaks the cue's + quotation integrity; truncate-while-any-source-survives was + considered and rejected \\ Graphic gesture & Anchor event & Re-anchor to nearest surviving event of same staff instance; for Free anchoring, no action; for Range anchoring, truncate & - proximity max: same staff instance \\ + proximity max: same staff instance. Truncate (ratified + Pass~12): a dead event-anchored range endpoint moves to its + containing region's edge --- a start endpoint to the region + start, an end endpoint to the region end, zero offset \\ Trajectory event & Endpoint pitch & Re-anchor: replace \texttt{EventPitch(PitchId)} with @@ -7039,7 +7165,10 @@ table is normative. Analytical annotation & Anchor & Re-anchor to time range preserving original extent; orphan if range cannot be reconstructed & - --- \\ + orphaning is the sanctioned outcome (ratified Pass~12) for a + wall-clock (region-relative) or indeterminate event span that + no stored \texttt{Range} anchor form can express without + region-origin resolution \\ Spelling attachment & Target pitch & Transition to tombstoned-target state & @@ -8579,6 +8708,21 @@ logical IR with composite objects flattened to individual glyphs, each glyph carrying its bounding box, anchor, and constraint inputs to the solver. +\begin{requirement} + \label{req:layoutir:constraint-floor} + \textbf{Minimal-tier constraint-emission floor (ratified Pass 12).} + The spacing pass \MUST{} emit at least the following constraint + set --- the \emph{Minimal-tier floor}, the testable acceptance + surface for the \texttt{Minimal} conformance tier + (Chapter~\ref{ch:solver}): no-collision chains between + successive notehead-bearing columns; per-glyph containment + within the owning region's frame; and one constraint per user + break override (system and page). Richer emission sets + (inter-band collision, kerned optical adjacency, duration- + proportional spring preferences) belong to higher tiers, whose + floors are defined when those tiers are ratified. +\end{requirement} + \subsection{Glyph-Level Objects} \begin{lstlisting}[language=Rust] @@ -10364,6 +10508,20 @@ pub enum OperationKindTag { SetUserPageBreak, DeclareTransaction, Registered(OperationKindRegistryId), + // Appended after Registered (append-only vocabulary; the + // tag's wire discriminants never reorder). Phase-2/Phase-3 + // operation-catalog growth: + InsertIdentifiedPitch, + DeleteIdentifiedPitch, + ModifyIdentifiedPitch, + CreateVoice, + DeleteVoice, + SetMetadata, + SetMetricGrid, + InsertStaff, + SetTimeSignature, + SetTempoSegment, + SetStaffLayout, } pub enum BarrierScope { @@ -10448,6 +10606,40 @@ pub enum BarrierCondition { preserving extension data by default. \end{requirement} +\begin{requirement} + \label{req:format:barrier-matching} + \textbf{Barrier matching for target-free and opaque operations + (ratified Pass 12).} + An operation with no graph target (\texttt{SetMetadata}, + \texttt{DeclareTransaction}) is matched by \emph{score-wide} + barriers only (\texttt{BarrierScope::WholeScore}); a barrier with + any narrower scope cannot match it, because there is no target to + test the scope against. An opaque \texttt{Registered} operation is + matched \emph{fully conservatively}: since its targets and effects + are unknown to a core implementation, it matches every active + barrier whose remaining predicates do not exclude it. +\end{requirement} + +\begin{requirement} + \label{req:format:unsafe-tombstone} + \textbf{Unsafe-edit tombstone semantics (ratified Pass 12).} + Crossing a barrier by an unsafe edit immediately deactivates the + owning extension's \emph{remaining} barriers for the editing + session --- the extension's invariants are already forfeit, so its + other barriers no longer protect anything. The crossing \MUST{} be + durably recorded at the next bundle commit: the pending tombstone + set survives the session that performed the unsafe edit. A + tombstoned extension that was declared \texttt{required = true} + leaves the bundle openable read-only by implementations that + depended on that extension (its invariants can no longer be + trusted); implementations that never understood it proceed under + the unknown-extension rules above. The manifest-side \emph{byte + encoding} of the tombstone record is deferred to the Binary Format + companion (the manifest schema is frozen at major~0, so the record + must ride an append-safe channel); see the companion's open + questions. +\end{requirement} + \section{Text Projection} \label{sec:format:textproj} @@ -11160,6 +11352,22 @@ pub enum SolverWarningKind { \texttt{Unsatisfiable}, with the partial layout marked as such. \end{requirement} +\begin{requirement} + \label{req:solver:subconformant-report} + \textbf{Sub-conformant report shape (ratified Pass 12).} + A below-conformance passthrough solver (\texttt{SolverTier::Stub}) + presented with declared constraints it does not evaluate reports + \texttt{SolvedWithWarnings} with + \texttt{satisfied\_hard\_constraints == false} and a warning + naming the unevaluated-constraint condition. The layout stays + renderable; the report makes no conformance claim and satisfies + no minimum-tier requirement. This is the one sanctioned case in + which a renderable status carries + \texttt{satisfied\_hard\_constraints == false}: the field reports + \emph{evaluated} satisfaction, and a non-evaluating solver has + nothing to claim. +\end{requirement} + \section{Constraint Families} \label{sec:solver:families} @@ -11190,6 +11398,21 @@ pub enum ConstraintStrength { vector. \end{requirement} +\begin{requirement} + \label{req:solver:kind-strength} + \textbf{Strength is kind-determined (ratified Pass 12).} + A constraint instance carries no strength field; its strength is + determined by its kind. Break constraints take their strength + from \texttt{BreakKind}: \texttt{Hard} $\to$ \texttt{Required}, + \texttt{Soft} $\to$ \texttt{Preferred} with weight $1.0$. Every + other core constraint family normalizes to \texttt{Required}. + \texttt{Registered} constraint kinds normalize conservatively to + \texttt{Required}. A future constraint family declares its + strength (and weight, if \texttt{Preferred}) in its normative + definition --- the strength channel is the \emph{kind}, not the + instance. +\end{requirement} + \subsection{Normative Constraint Families} The constraint catalog (spring, collision, alignment, containment, @@ -13555,13 +13778,16 @@ divergence. The specification fixes this at the encoding layer. \section{Open Algorithm Hooks} \label{sec:det:open} -Several algorithms remain open questions in the body of the -specification: the spelling pre-pass (Chapter~\ref{ch:pitch}), -the notational decomposition algorithm -(Chapter~\ref{ch:time}), tempo integration for arbitrary curves +Two algorithms remain open questions in the body of the +specification: tempo integration for arbitrary curves and root-finding for \texttt{wallclock\_to\_musical} (Chapter~\ref{ch:time}). These algorithms affect canonical -state. +state. The spelling pre-pass (Chapter~\ref{ch:pitch}) and the +notational decomposition algorithm (Chapter~\ref{ch:time}) +were resolved in Pass~12 to the profile-declared disposition +below, each with a ratified default identifier +(Requirements~\ref{req:pitch:spelling-algorithm} +and~\ref{req:time:decomposition-algorithm}). \begin{requirement} Any algorithm that affects canonical state \MUST{} satisfy one @@ -14744,6 +14970,33 @@ layouts they own versus inherit: which region governs a cross-region slur's spanning permission (implemented as conservative AND, P12-K12). \\ + \today & Pass 12 G-ratification (the batch pass) & + Retires the open Pass-12 batch (28 rows; dispositions in + \texttt{PASS12\_RATIFICATION\_LOG.md}). Algorithm dispositions: the spelling + pre-pass and notational decomposition move to profile-declared-by-versioned- + identifier with ratified v1 defaults + (Requirements~\ref{req:pitch:spelling-algorithm} + and~\ref{req:time:decomposition-algorithm}; closes P12-H1/H3/H4/H5/C5); + decomposition precedence pinned fixed (P12-H6); authored annotations for + inference-ineligible targets surface + (Requirement~\ref{req:pitch:authored-uninferred}, P12-H7). Identity: + system-derived intrinsic content immutable under reduction (P12-K3). Genesis + ratified outside the operation set (P12-K8). Cross-region slur permission = + AND (P12-K12). Re-anchoring: cue cascade-on-any-source, Range truncate + defined, annotation orphaning sanctioned, \texttt{ReanchorReason:: + SameCanvasNearer} appended (P12-C1/C2/C3/C4). Barriers: target-free and + opaque-operation matching (Requirement~\ref{req:format:barrier-matching}, + P12-E4); unsafe-edit tombstone semantics + (Requirement~\ref{req:format:unsafe-tombstone}, P12-E5; encoding deferred to + the Binary Format companion). Solver: kind-determined strength + (Requirement~\ref{req:solver:kind-strength}, P12-I4), sub-conformant report + shape (Requirement~\ref{req:solver:subconformant-report}, P12-I5), the + Minimal-tier constraint floor + (Requirement~\ref{req:layoutir:constraint-floor}, P12-I6). The stale + \texttt{OperationKindTag} listing gains the eleven appended tags. Companion + movements: Operation Catalog 0.5.0~$\rightarrow$~0.6.0, Binary Format + 0.3.0~$\rightarrow$~0.4.0. + \\ \bottomrule \end{longtable} diff --git a/spec/operation_catalog.pdf b/spec/operation_catalog.pdf index 93306ce..dbcd24b 100644 Binary files a/spec/operation_catalog.pdf and b/spec/operation_catalog.pdf differ diff --git a/spec/operation_catalog.tex b/spec/operation_catalog.tex index d4d84d2..d34570b 100644 --- a/spec/operation_catalog.tex +++ b/spec/operation_catalog.tex @@ -226,7 +226,7 @@ {\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.5.0 --- Phase 2/3 (K0 representative + broad-K0 M2 groups + Phase-3 first tranche)}\\[4pt] + {\normalsize\color{epiphanyink}Version 0.6.0 --- Pass-12 G-ratification (meta-operation edge semantics, system-derived immutability, recreate vocabulary, slot retirement)}\\[4pt] {\small\color{epiphanyslate}Normative for the operation kinds it defines} \vfill \end{titlepage} @@ -287,10 +287,23 @@ specification revision. The core specification changes only when the 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 remaining (Phase-3) framework slots 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 one -of those slots it does not implement. +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}. % =========================================================================== \chapter{The Catalog Framework} @@ -469,17 +482,17 @@ 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} +\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} @@ -512,6 +525,17 @@ graph (re-sorting a non-metric voice is a deferred refinement); a malformed 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. @@ -581,7 +605,11 @@ general case (interval composition need not commute), so the resolved value is the composition in canonical reduction order. In this prototype \texttt{chromatic\_steps} is a minimal CMN alteration shift that commutes except at the alteration's \texttt{i8} saturation bound; rich interval algebra is -deferred (Chapter~4 tuning catalog; P12-K2). +deferred (Chapter~4 tuning catalog; P12-K2). \emph{Pinned (Pass~12):} the +prototype semantics --- CMN alteration shift with documented \texttt{i8} +saturation --- are the declared v1 behavior; replacing the payload with a +faithful interval representation is a \emph{payload schema-major} under the +Binary Format companion's evolution rule, landing with the tuning catalog. \textbf{Conflict cases.} None --- composition is deterministic in canonical order (a deterministic repair, not a conflict). @@ -617,6 +645,13 @@ 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). @@ -772,7 +807,13 @@ 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. Graph-aware reduction additionally preconditions that the +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 every differing-value re-create: the carried \texttt{TimeSignature} +(Section~\ref{sec:k0:meter-tempo}) and the structural-container creates +(Section~\ref{sec:k0:structural-containers}). 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. @@ -885,7 +926,8 @@ present) the length-framed \texttt{segment}. 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. The time-signature value's beat-group sum is validated at +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 @@ -976,6 +1018,19 @@ action of \texttt{Dismiss} reaches the \texttt{Dismissed} state; any other actio 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 @@ -1016,6 +1071,17 @@ operations in \texttt{caused\_by} --- the same discipline as \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). @@ -1027,8 +1093,11 @@ 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 remains unpinned and unimplemented --- -a Pass-12 question (P12-K5). +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)} @@ -1066,6 +1135,25 @@ 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); @@ -1151,17 +1239,18 @@ a fresh design. creation, LWW structural overwrite, and LWW advisory respectively. \end{description} -\section*{Remaining framework slots (unavailable, MUST reject)} +\section*{Retired slots (ratified Pass~12: outside the operation set)} \begin{description} - \item[Create score / canvas] - The document root and the canvas are \emph{inline singletons}, not - id-addressed objects: \texttt{TypedObjectId} has no Canvas kind, and the - root is never op-minted --- genesis today is the empty-document - constructor plus bundle creation, outside the operation set. Turning these - into operations requires an addressable root/canvas object model (a - graph-model decision for G, filed as a Pass-12 row), so the slots remain - deliberately unavailable rather than force-designed. + \item[Create score / canvas --- retired, closing P12-K8] + The document root and the canvas are \emph{structural givens}, not + operation products: \texttt{TypedObjectId} has no Canvas kind, the root is + never op-minted, and genesis is normatively the empty-document constructor + plus bundle creation, outside the operation set (core specification, + Chapter~5 \sectionsc{The Canvas}). 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}