ENGRAVER_VERSION 11 -> 12. The inter-staff solve now closes a slack pair as well
as opening a crowded one, realizing the InterStaffGap band's declared height
exactly. SYSTEM_STAFF_PITCH is demoted from a floor to an initial arrangement the
solve fully renegotiates. This is what vertical_density_penalty was reporting: an
un-pressured multi-staff system sat at 0.739, honest sprawl against the declared
gap, because the axis is symmetric and the solve only ever expanded.
The band's height had no agreed meaning, so pin it: it is an INK CLEARANCE -- the
separation between the two staves' outermost content, exactly the unit
req:qmc:vertical measures. preferred 2.0 -> 5.0, min 1.0 -> 2.0. The old 2.0 was
a placeholder reconciled with nothing: neither the 8.0 staff-box gap the fixed
pitch of 12 produces, nor the ~6.4 ink clearance it leaves for plain content.
Realizing it would have crushed a relaxed system to a pitch of ~7.6. At 5.0 plain
ledgered content settles near a pitch of 10.6.
Making the solve two-sided immediately exposed a CASCADE DEFECT latent since v11.
The recurrence subtracted the upper staff's shift from the measured gap and then
added it back through the accumulator, so every pair below the first was
over-separated by exactly the shift above it. Both staves move; the relation is
shift_lower = shift_upper + target - (upper_lo - lower_hi), the UNSHIFTED gap.
three_staff_close_content's lower pair realized 21.06 against a declared 4.0. It
was invisible on two-staff fixtures (shift_upper = 0) and invisible to
inter_staff_shifts_cascade_down_three_staves, which asserted only s2 > s1 -- true
under both the correct and the double-counting recurrence.
What caught it was the metric measuring realized clearance back from the BAKED
output instead of the solve's own extents. Reading back solver intent would have
reported 0 and shipped the over-separation again. That design choice was made one
commit earlier for exactly this reason; the catalog rationale now recommends it to
any conforming implementation.
Once the solve realizes each declared clearance exactly, every inter-staff unit is
0 on a healthy solve -- the axis becomes a solver self-check, and its MEAN can no
longer distinguish "measured every realization" from "measured one". So
vertical_raw is split into vertical_units and the regressions assert the unit SET.
Four mutations verified: the double-counting recurrence, expand-only, the
glyph-members band filter, and first-system-only measurement each fail a named test.
No normative change, no version move: QMC formula, units, anchors, thresholds all
untouched; only its non-normative rationale is refreshed. Churn is the two
multi-staff engrave goldens: two_staff grew by exactly 3.0 (the target change, no
cascade); three_staff SHRANK by 9.06 -- the same +3 per pair, less the 17.06 of
over-separation the defect was adding. Single-staff and every stub golden are
byte-stable.
The 5.0 was the user's call. 4.0 ("one staff height") was chosen first and
withdrawn once its true consequence -- pitch 9.57, not the 11.04 an arithmetic slip
of mine had projected -- was measured rather than inferred. The slip: deriving
plain-content ink clearance from an aggregate metric by assuming two contributing
units when it had three.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review follow-up on 43215c7. Both findings were right, and the second falsified a
comment I wrote in that very commit.
1. Region staff bands were still identified by glyph `members`. vertical_raw
measured content over all primitives but decided WHICH staff bands belong to a
region by glyph membership -- reintroducing the assumption the change exists to
shed. A staff band is allowed to own no glyphs: to_constrained emits one per
staff of the region regardless, and a percussion-clef staff (no bundled glyph,
so it engraves to a traced anchor stroke) with no notes owns only staff-line
strokes. Membership now comes from content presence in one of the region's
systems, which identifies the band exactly -- a staff band is per-(staff,
region), so its content can land nowhere else.
2. Only the first realizing system was measured, justified by a comment claiming
rigid system translation makes every realization agree. The inter-staff solve
had just falsified that: it sizes each system's gaps from that system's own
content. req:qmc:vertical now counts ONE UNIT PER REALIZATION, matching how
realized inter-system gaps were already counted. That is a contributing-unit
change, so unlike 43215c7's clarification the catalog moves: QMC 0.2.0 -> 0.3.0
(the P12-I12 precedent). Raw formula, anchor, orientation, thresholds unchanged.
Two new fixtures, because an unexercised fix is what I criticised last round:
percussion_placeholder_staff (a valid, invariant-clean score whose lower band owns
zero glyphs and six strokes) and two_staff_wrapping_pressure (one region, two
systems, staff-line gap 15.93 where it collides and 7.87 where it is slack). Both
mutation-verified: the members filter scores 4.8e-7, first-system-only scores
1.3e-7 -- each ~0 where the corrected axis reports real deviation.
What the per-realization count exposes is not comfortable, and is recorded rather
than smoothed over: two_staff_wrapping_pressure now scores 0.739. Its pressured
system solves to the declared gap exactly; its slack system sits at ~5 staff
spaces against a preferred 2.0. The axis is symmetric -- a gap wider than
preferred is sprawl exactly as a narrower one is crowding -- and this solve only
expands, never compresses. The deferral "compressing an OVER-wide fixed gap toward
preferred ... rarely wanted" is promoted to measurably wrong. Named, not fixed:
compression is a layout change (golden churn, ENGRAVER_VERSION move), not a
measurement one.
Adjacent finding, parked: Staff::default_clef is never consulted -- to_constrained
takes the active clef from the instance's clef_sequence and falls back to
Clef::default() (treble), so a staff declaring its clef only on Staff engraves as
treble. Verified (no layout-ir consumer reads the field). Filed in layout-ir
DECISIONS with the ConstrainedLayoutIR listing gap, pending a >=3-candidate batch.
Measurement-only: no layout change, ENGRAVER_VERSION stays 11, zero golden churn.
Gate green; QMC PDF rebuilds clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two items the inter-staff solve deferred turned out to be one thing -- and it was
not the "metric-vs-solver tension" I filed it as. The catalog was right; the
engraver was non-conforming.
req:qmc:vertical has always defined the realized inter-staff gap as the separation
"between the adjacent CONTENT EXTENTS the band separates". vertical_raw measured
the separation between the two bands' glyph `members`, because until primitive
band ownership (efaebb9) a band listed no strokes or curves to own. A staff's
outermost ink is usually not a glyph. On two_staff_close_content the solve cleared
the declared 2.0 gap exactly, while the glyph-ink gap was 5.06 -- so the axis
reported |5.06-2|/2 = 1.53, saturated to 1.0, and fired a Standard-tier floor
warning on a correct layout. The metric was charging the solver for the ledger and
slur ink it had made room for. Axis now reads 2.7e-7; the warning is gone.
Two design calls worth naming:
- The geometry is read back from the BAKED output, not from the solve's own
staff_ext. Reading back solver intent would make the axis circular and blind
to exactly the bug class that bit twice this week; now a shift the bake fails
to apply to some primitive class surfaces as a real deviation. CastLayout
gained stroke_system/curve_system for it -- a stroke carries no spring slot,
so system_of_slot cannot answer for it.
- The solve now targets the preferred_height of the InterStaffGap band
to_constrained emitted for that staff pair, not VerticalBand::inter_staff_gap's
default. That is what makes the band a height model rather than a constant:
solve and metric agree by construction, not by both calling one constructor.
NO version move. Formula, contributing units, anchor, and normalization are
unchanged -- only a wrong measurement was. This is the P12-I11 precedent
(engrave-side resolution), not P12-I12 (which redefined spacing_distortion's unit
and did move the catalog). The catalog gains a clarification of what "content
extent" means, since before band ownership that reading was arguably
unimplementable, which is why the defect survived. Its stale rationale (still
claiming the vertical spring solve is deferred) is refreshed, and the axis's
inter-system half is recorded as a genuine trade-off against page_fill_efficiency
rather than a defect, so it is not re-filed as a bug.
Measurement-only: no layout change, ENGRAVER_VERSION stays 11, zero golden churn.
Mutation-verified: reverting vertical_raw to glyph-only measurement scores 1 and
fails the new assertion. Gate green, QMC PDF rebuilds clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ced4b72's ratification log said the ConstrainedLayoutIR listing gap was "tracked
as a Pass-13 candidate", but PASS13_CANDIDATES.md declares that batch CLOSED with
no open candidates -- and the house rule opens a pass only at >=3 candidates. The
claim pointed at a ledger that says the opposite.
Filed where the rule wants it instead: layout-ir DECISIONS records the two fields
the listing still elides (break_origins, catalog), why neither blocks an
implementer (both are governed by requirement text elsewhere), and that it is
PARKED awaiting company rather than reopening a pass on its own. The log now says
that too.
No code, no spec text.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review follow-up on fc411ea. Finding 1 was correct and was mine to own: I wrote
"every primitive -- glyph, stroke, and curve alike -- MUST declare a
VerticalBandId" and placed it in the ResolvedLayoutIR section, where the adjacent
ResolvedGlyph listing has no such field. Under a literal read the implementation
was non-conforming against a requirement I had just added.
The rule belongs to the stage that has the obligation. req:layoutir:primitive-
band-ownership moves to Ch7 §ConstrainedLayoutIR §Vertical Bands and is scoped to
"every primitive the projection presents to the solver". A new companion,
req:layoutir:resolved-band-ownership, says what survives: a resolved Stroke/Curve
MUST retain its band (casting-off and the inter-staff solve both relocate them
and must attribute them), while a ResolvedGlyph carries none -- its ownership is
consumed during the solve and already baked into its resolved position, so the
field would be dead weight in a rendering fingerprint. That requirement also
carries the non-canonical clause: canonical_bytes omits vertical_band even from
the primitives that do retain it.
Finding 2: the ConstrainedLayoutIR listing gains strokes/curves. They have been
in the code since staff lines and were never listed -- so an implementation
following Chapter 7 could not have supplied non-glyph ownership to the solver at
all, which makes this the listing gap that actually blocked the new MUST.
Finding 3: the ratification trail now records the tranche -- a revision-history
row and a PASS12 log section naming both requirements, the three listing
corrections, the two commits, and the fact that adopting it churned no golden
(the declared owner agrees with the inferred one across the corpus, which is what
licensed the swap).
The log also records a pre-existing listing gap I found while checking my own
citations: ConstrainedLayoutIR still elides break_origins and catalog. I had
first written that neither is named anywhere in core_spec, then verified and
found both types ARE specified (req:layoutir:break-origin-attribution; Ch7 §Glyph
Catalog Identity) -- only the struct listing is incomplete, and unlike
strokes/curves neither gap blocks an implementation. Left as a Pass-13 candidate
rather than widened into this tranche.
Spec rebuilds clean: no LaTeX errors, no undefined references, both new labels
registered. Workspace 30/30, conformance 8/8; no Rust changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The three slur refinements (dashed rendering, curve splitting, slur_shape
measured) lifted into the spec — Chapter 7 / Quality Metric Catalog only,
non-canonical, so no wire form and no companion-version move.
core_spec Ch7: req:layoutir:slur-curve extended — an authored non-Solid
SpanStyle line renders faithfully (the LineStyle rides the Curve, whose listing
gains the field); a slur spanning a system break splits into per-system
sub-curves (de Casteljau; first segment keeps the slur's provenance, the rest
synthesized continuations). Only the curvature-computing algorithm stays
forward-referenced out now (dash rendering no longer deferred).
quality_metric_catalog: the §slur_shape rationale and the notated-but-unrendered
open question refreshed to record slurs now render and are measured; the formula
(req:qmc:slur) is unchanged, so no catalog version move.
Revision-history row (Push 3); a Push-3 tranche in PASS12_RATIFICATION_LOG.md;
layout-ir DECISIONS cross-ref extended. Both specs build clean (latexmk
-xelatex, 0 undefined refs). No code change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The schema-major-2 push's rendering consumers (E1 repeat barlines/volta
brackets, E2 slur cubic-béziers) lifted into core spec Chapter 7 — layout-IR
only, non-canonical, so no wire form and no companion-version bump (Operation
Catalog stays 0.7.0, Binary Format 0.6.0), mirroring the schema-major-1 Phase-F
engrave-ratify-only precedent.
Three req:layoutir:* requirements added at §ResolvedLayoutIR:
- resolved-primitives: ResolvedLayoutIR carries non-glyph line Strokes (staff
lines, stems, barlines, volta brackets — present since staff lines, never
previously ratified) and cubic-bézier Curves (slurs, E2); each provenance-
traced (the hit-test basis), re-spaced like a glyph, non-canonical. The
struct listing gains strokes/curves and the Stroke/Curve shapes; the RenderIR
provenance requirement widened from "originating ResolvedGlyph" to
"ResolvedGlyph, Stroke, or Curve".
- repeat-render (E1): a barline-drawing RepeatStructure renders a repeat barline
at each resolved boundary, each Volta a bracket with ending numbers;
unresolvable boundaries draw no ink (honest placement); jump-kind marks and
cross-region repeats deferred.
- slur-curve (E2): a Slur renders as a cubic bézier honoring CurvatureOverride;
an endpoint not on a single staff of one region draws no curve; a non-Solid
line style MUST be surfaced (diagnostic), not silently rendered solid.
The curvature-computing algorithm and the RenderIR encoding stay forward-
referenced out. Ch5 §Slurs / §Repeat Structures and the Ch7 Forward References
gain labels for the cross-references. Revision-history row + a schema-major-2
rendering tranche in PASS12_RATIFICATION_LOG.md; layout-ir DECISIONS carry the
Phase-F cross-refs. Spec builds clean (latexmk -xelatex, 0 undefined refs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-commit review finding: req:pitch:spelling-algorithm /
req:time:decomposition-algorithm ratified MUST-error for unregistered
algorithm ids, but derive_annotations kept the pre-ratification
derive-nothing-under-honest-profile behavior (and a test locked it).
The spec text stands; the code moves: derive_annotations returns
Result<DerivedAnnotations, PrePassError>, rejecting unregistered ids
up front — a silently-empty derivation is indistinguishable from a
legitimately empty score, and would silently disagree with an
implementation that does support the requested id. All production
callers use the default profile (.expect); the stale lock test is
rewritten as unknown_algorithm_ids_error; PrePassError re-exported.
Full gate green: fmt, clippy -D warnings, rustdoc -D warnings, 30
workspace suites, conformance scale 1 (8/8).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
Spec/process only, no code. Closes the schema-major-1 track (Phases A-F): the
two data-model batch rows parked behind the frozen-layout rule (P12-I7, P12-K7)
landed via Phases C/D1, and the three engrave/layout-ir dispositions the
implementation already made are ratified into the core spec.
- core_spec: three normative requirements ratifying the implemented behavior --
break-constraint satisfaction (req:layoutir:break-satisfaction, I8: a
SystemBreakAt/PageBreakAt is satisfied iff the final ResolvedLayoutIR starts a
system/page at that slot), break-override attribution via a
ConstrainedLayoutIR.break_origins sidecar declining to widen the constraint
record (req:layoutir:break-origin-attribution, I9), and system-continuation
synthesis Registered(SYSTEM_CONTINUATION_SYNTHESIS) with an (original, ordinal)
instance key (req:layoutir:continuation-synthesis, I10). Revision-history row.
PDF rebuilt (latexmk -xelatex, 0 undefined refs).
- PASS12_RATIFICATION_LOG: schema-major-1 tranche (I7/K7 landed + I8/I9/I10
adopt), with the open cross-region-slur item flagged.
- PASS12_BATCH: struck I7/K7/I8/I9/I10; added P12-K12 (which region governs a
cross-region slur's spanning permission -- implemented as conservative AND).
- engrave/layout-ir DECISIONS: ratification cross-refs; the P12-I7 note reworded
so deferred Phase C' does not read as landed.
- .gitignore: spec/*.xdv (xelatex intermediate; the tracked PDF is committed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
The Standard-tier spacing floor warned on short healthy scores: three
reference-suite entries measured spacing_distortion 0.36-0.41, above the
0.32 warning floor (0.8 x 0.40), a spurious diagnostic (every score still
passed Minimal). Root cause: the CV folded in the clef-to-first-note lead
advance, which is sized by notational-furniture width, not by rhythm.
Unlike P12-I11 (an engrave-side fix, no spec change), the defect here lived
in the metric's own normative definition, so the honest fix is a catalog
change - the mirror of I11: correct the measurement rather than relax the
threshold.
Quality Metric Catalog 0.1.0 -> 0.2.0: spacing_distortion is scoped to the
system's rhythmic columns (spring slots bearing a notehead or rest). The
clef / key-signature / time-signature lead and barlines contribute no
column, so a note-to-note advance spans them. quality::census now builds its
spacing columns only from a precomputed rhythmic-slot set (is_rhythmic: a
notehead*/rest* glyph anywhere in the slot); the CV and the >= 3-column
contributing-unit rule are otherwise unchanged.
Results: RS-3/5/6 drop to 0.2188 / 0.0819 / 0.0856 (all below the floor),
and the axis stays honest on real irregularity (RS-3 keeps 0.2188 from a
mid-line accidental). RS-2/RS-4 go vacuous-0.0 (their systems carry < 3
rhythmic columns - honestly "too little to measure," cleaner than the old
furniture noise). RS-1 0.1341 -> 0.1967 (cross-barline note advances).
The 1.0 anchor, orientation, range, tier thresholds, and the eight other
axes are unchanged. This is measurement-only: the resolved layout, canonical
bytes, render goldens, and ENGRAVER_VERSION are all untouched - only the
reported spacing_distortion value moves. The duration-aware optical-spacing
open question (deviation from duration-proportional spacing) stays open; it
needs the pipeline's deferred duration-aware preferred widths.
- catalog: spacing_distortion requirement (req:qmc:spacing) redefined over
rhythmic columns, rationale + open-question note updated, version 0.2.0,
revision-history row; PDF rebuilt clean (0 undefined refs).
- engrave: quality::census rhythmic-column filter + is_rhythmic; module and
spacing_raw docs; the floor-column contrast test re-pointed from b-flat's
spacing (no longer warns) to RS-1's casting-off (still between the Standard
0.28 and Minimal 0.72 floors); new short_scores_do_not_trip_the_standard_
spacing_floor locks the fix.
- QMC version breadcrumbs bumped to 0.2.0 (engrave + layout-ir quality.rs,
both DECISIONS.md, testkit RS-1 comment); reference suite companion
unchanged (cites the catalog by name, no pinned values).
- process trail: PASS12_BATCH I12 struck; PASS12_RATIFICATION_LOG I12
section (Version movements: QMC 0.1.0 -> 0.2.0); engrave DECISIONS quality
decision 8 + item 7 + candidate.
861 workspace tests pass; clippy -D warnings, fmt --check, rustdoc
-D warnings, and the catalog PDF build all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
RS-1 honestly failed the Minimal casting_off_quality threshold under the
reference engraver: greedy first-fit left a two-measure stub last system
(width CV 0.6145 -> clamped 1.0 > 0.90). Cleared the honest way — an
engrave-side balance pass, no Quality Metric Catalog or core-spec change.
Casting-off gains a second phase, a widow rebalance
(casting::rebalance_widows, run between the greedy walk and vertical
stacking): it moves whole trailing measures from a region's penultimate
system into its final one, choosing the shift that minimizes the larger of
the two distribution penalties the catalog defines for the break family —
the width imbalance (casting_off_quality, the CV of the region's system
widths) and the non-final break penalty (system_break_penalty, the mean of
|W-w|/W over non-final systems). distribution_cost computes each raw by the
same formula as quality.rs's casting_off_raw / system_break_raw (mean not
worst, abs not clamp), so the rebalance optimizes the values the metric
census will report. The two axes pull against each other, so their min-max
lands on a 6/4 split for RS-1 (casting_off 1.0 -> 0.4463, system_break
0.254 -> 0.677, every axis <= 0.90) — with comfortable margin, over the
fragile full-balance 5/5 (system_break 0.889, a hair under 0.90).
Scope is tight: only a region's last boundary moves, and only when greedy
placed it (an Automatic boundary with no break requirement or page force
pinned to its slot); a user/IR-anchored or page-forced boundary is never
disturbed, the penultimate system keeps >= 1 measure, the final never grows
past its predecessor, and the system count is unchanged — so every
break-count and page-assignment invariant (and all break-constraint tests)
hold untouched.
The casting_off 0.5 anchor and the 0.90 Minimal column were vindicated, not
relaxed: the engraver improved, no anchor rescale / threshold loosening /
RS-1 override. Core spec Chapter 9's "Minimal makes no optimality claim"
already permits the heuristic, so nothing normative changed (no .tex/PDF
rebuild). P12-I12 (the Standard-tier spacing floor on short scores) stays
open.
- engrave: rebalance_widows + distribution_cost + two-phase module docs;
ENGRAVER_VERSION 2 -> 3 (a wrapping score's baked geometry differs from
pure greedy); three new casting tests (even-split preference, the
mean-not-worst break penalty for 3+ systems, and the resolved final
system); the_wrapping_fixture_is_measured_honestly re-pinned to the 6/4
values (both axes floor-warn under Standard, status untouched).
- testkit: RS-1 minimal_xfail row removed (promoted to a plain Pass); the
suite ships no xfail rows.
- render-svg: ten_measure.engrave.{svg,snapshot} goldens regenerated
(view_box width 83.99 -> 64.95; still two systems).
- process trail: PASS12_BATCH I11 struck; PASS12_RATIFICATION_LOG
"no spec change" section; engrave DECISIONS casting-off decision 9 +
quality item 7 + candidate promoted.
860 workspace tests pass; clippy -D warnings, fmt --check, rustdoc
-D warnings all clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
Two audit pushes whose code edits interleave line-by-line in the same
files (reduce.rs, bundle.rs, the DECISIONS logs), committed together so
the tree at every commit builds. Gate: 784 workspace tests pass, clippy
-D warnings clean, fmt clean.
Push 1 — the true MUST violations, all fixed:
- bundle: zstd read support on both read paths, output bounded by the
declared uncompressed_length, typed decompression errors, explicit
CompressedManifest rejection (zstd 0.13 workspace dep; write path
stays uncompressed per the Phase-3 deferral).
- ops: system-derived counter collision check — mint registry seeded
from the base graph, canonical-order pre-walk, halt via the new
PendingReason::HaltedBySystemCollision (discriminant 4, additive)
with transaction-atomicity and causal-dependent closure; neither
input set occupies a collided counter. canonical_pitch_bytes made
pub in core for the MUSCSPCH preimage.
- ops: Transpose skips tombstoned targets per the catalog; missing
targets still refuse the whole operation.
- ops: marker re-anchoring recorded as a RepairRecord in the
triggering operation's effect; ResolveConflict meta-conflicts name
both resolvers; base-free pitch-id freshness; reserved effect
vocabulary annotated.
- core: decomposition pre-pass honors authored attachments
(resolve_decomposition, spec-default precedence); inversion
tolerance typed as a TempoIntegration-class Tolerance.
- CONFORMANCE.md: the determinism conformance statement required by
Appendix D — all seven declarations.
Push 3 — wiring the types-only machinery:
- layout-ir/engrave: to_constrained emits real constraints (successive
notehead no-collision chains, per-glyph region containment, soft
user-break constraints); ConstraintStrength{Required, Preferred}
with strength-by-rule; Preferred violations surface as warnings, not
failures; StubSolver reworked honest-but-renderable. SVG goldens
byte-identical; snapshot constraint counts regenerated (0->90/15).
- layout-ir: to_logical projects user system/page breaks as anchored
EngravingOverrides with paired UserOverride-sourced decisions
(OverrideKind::SystemBreak/PageBreak carry TimeAnchor, ratified in
the spec alongside).
- layout-ir/ops/editor-core: edit-barrier bridge — decode mirrors for
the whole barrier tree (reject-never-normalize, NFC revalidation,
MAX_CONDITION_DEPTH = 64), golden-locked blob codec for the
ExtensionDeclaration fields, a barrier gate in apply and
apply_transaction backed by a Score oracle and real containment
contexts, and apply_unsafe recording the crossed extensions in
extensions_requiring_tombstone() for the next bundle write.
- ops: ResolveEquivocation meta-operation per the newly ratified
catalog entry — payload discriminant 3 (appended), set-level
earliest-resolve-governs promotion, ResolveConflict-mirrored
meta-conflicts, permutation-invariance fuzz; the missing golden
locks on the OperationKind/OperationPayload wire tables added.
- ops/editor-core: validation modes — ValidationMode + a non-canonical
advisory layer (validate.rs), an authoring gate before minting, and
reduction pinned as replay mode by construction (canonical bytes
untouched).
- bundle: the operation index (opindex.rs) — provisional golden-locked
payload, binary-search locate, staleness defined as full-ChunkRef
set equality against operation_roots, and the reject-and-rebuild
discipline (a defective index is never bundle corruption).
- ops: re-anchoring rule table completed — the four-key "nearest"
ordering computed from base-free ledger indices; markers re-anchor
to the nearest live event in the same staff instance (replacing the
Push-1 region-start stand-in); cue-source cascade; graphic-gesture
Events/Range/Free rows; comment and analytical-annotation orphaning.
Zero appended discriminants.
Spec enablers ratified with Push 3: catalog §ResolveEquivocation
(0.3.0 -> 0.4.0) and anchored break overrides; 16 new Pass-12 rows
filed (C1-C4, K5-K7, I4-I6, D1, E1-E5). The data-model payload
expansion (SlurKind, beam geometry, voltas, instrument bodies,
metadata) is deliberately staged to the Binary Format companion — the
positional graph codec has no value-level versioning, so filling those
structs is a schema-major break that should land once, with J.
Also carries the pre-existing editor-track increment: the atomic
tuplet overwrite (CascadeDeleteTuplets prunes decomposition
attachments naming the cascaded tuplet).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
The 2026-07 compliance audit's spec-side batch: every place where
normative text trailed a disposition the project had already made (the
ratified Operation Catalog, a crate DECISIONS entry, or the
PHASE2_QUICKSTART model the implementation follows). No byte-layout or
architecture changes.
- Re-anchoring table: the Slur/Spanner rows now state the catalog's
surviving-endpoint semantics (re-anchor while >=1 endpoint survives,
cascade-delete only when none does).
- RewriteTuplets adopts the catalog's id-only v1 payload (TupletRewrite
removed); graph-aware reduction MUST refuse the variant rather than
fabricate rewritten values.
- Operation-envelope blocks are pure envelope vectors (a content-
addressed chunk cannot embed its own id); per-block summaries
(dvv_summary/min_stamp/max_stamp) relocated to the manifest's
operation_block_summaries map; the order-blocks-by-min_stamp SHOULD
dropped (the canonical encoding sorts chunk refs by encoded form).
- Ch2+Ch3 pre-passes respecified as canonical derived annotations —
deterministic functions of (graph, profile, versioned algorithm id),
recomputed on materialization, never stored; the incremental-re-run
MUST demoted to MAY with unobservability required.
- Spelling precedence ties break by canonical attachment order (the
creation-timestamp tie-break deleted; attachments carry none).
- ConflictRegistry moved off the Score root into canonical materialized
state, matching Chapter 6.
- SolverTier gains the non-conformance Stub variant.
- The drafted ModifyEvent M2e text landed in the catalog (0.2.0 ->
0.3.0): metric placement changes materialize behind a voice-occupancy
placement precondition.
Dispositions logged in spec/PASS12_RATIFICATION_LOG.md; the Pass 12
batch itself stays open (no P12-H/K rows resolved). Also carries the
audit-filed batch rows P12-H6/H7/K3/K4.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd