An adversarial review of the slur-quality tranche found a real interaction bug
between curve splitting (commit 5869691) and slur_shape measurement (7d61271):
slur_shape_raw iterated cast.curves — the per-system SUB-CUBICS of a
break-spanning slur — and measured each fragment as a unit. A slur that is
ideally shaped as a whole (ρ ≈ 0.16, in-band) splits into sub-arcs whose
diagonal chords each read flatter (ρ below 0.08), so the whole slur earned a
spurious "too flat" penalty (confirmed ~0.088) and was double-counted —
contradicting the catalog's "a tier that draws the ideal shallow arc measures 0"
property.
Fix: measure the WHOLE slur curves of the constrained input.curves (one unit per
drawn slur — the engraver's arc-proportion decision), not the cast fragments.
Casting's horizontal re-spacing and system-splitting are spacing/rendering
concerns, not shape ones. The Quality Metric Catalog contributing-units
definition is clarified: the unit is the whole slur, measured once even when
split across a break.
Regression: a break-spanning in-band slur splits (≥2 segments) yet measures 0.
Still measurement-only (no ENGRAVER_VERSION bump, no golden churn, RS suite
unaffected). 936 tests, conformance 8/8.
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 first phase of the first-ever binary-format schema-major bump (v0 -> v1),
the machinery-first minimal major. Spec-only: ratifies the contract that
Phases B-F build against; no code changes.
core_spec.tex: defines the two referenced-but-undefined types that were the
P12-I7 / P12-K7 gaps -- CanvasLayoutDefaults (with core geometry primitives
CanvasSize/CanvasMargins in staff spaces via CanonicalF64, A4/8mm default,
since core has no geometry types and must not depend on layout-ir) and
PitchRange (advisory pitch compass used by Instrument.range and
IndeterminacyHints) -- and adds Region.permits_spanning_slurs (default false).
Records schema major 1 as the first data-model expansion major and tightens
the minor-version rule (a field add, even Option, is major; minor = append
discriminants to the companion's append-safe vocabularies only).
binary_format.tex -> 0.3.0: the full "Schema Major 1" section --
- Where the changed fields reach: Canvas.layout_defaults and
Instrument.range are snapshot-only (no CreateCanvas/CreateInstrument op),
but Region.permits_spanning_slurs also reaches the CANONICAL CreateRegion
operation payload (CreateRegion embeds the full Region). The canonical-base
MaterializedState embeds none of these and stays major 0, byte-identical.
- Cross-major reader rules: discard-and-regenerate non-canonical chunks;
parse-or-read-only for canonical ones, so a major-0 reader opens a bundle
carrying v1 CreateRegion ops read-only.
- Accept-set gate [min,max] (rejects majors outside the set); per-payload-
type major assignment; the changed v1 value layouts (the wire form ratifies
the reduced reference-code layout, not the fuller data model); the total
default-filling v0->v1 migration table (including the CreateRegion payload).
- Length-prefix unification NARROWED to the resolved-layout (its own
non-canonical LayoutCache): the barrier/extension blobs stay regime (b) u64
because they ride the canonical manifest, which stays major 0.
Two review passes hardened this checkpoint. The first caught that Region is a
canonical operation payload (not cache-only, as the architecture analysis had
assumed) -- user chose to embrace it and build the canonical op-payload
migration. It also surfaced the barrier-blobs-in-manifest constraint that
narrows the unification. The second refined the minor-version delegation, the
accept-set outside-[min,max] semantics, and stale "no defined type" text in
the reference-suite / quality-metric companions and the engrave DECISIONS.
P12-I7 moved to IN PROGRESS (spec type defined here; code graph home lands in
Phase C). Both companions and the engrave DECISIONS reworded accordingly. All
four affected PDFs rebuild clean (0 undefined references).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
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
857 workspace tests pass; clippy -D warnings, fmt, and rustdoc clean;
both new companions build with zero undefined references.
Quality Metric Catalog v0.1.0 (spec/quality_metric_catalog.tex, new):
- Formal definitions for all nine normative quality metrics, each with
a raw measurement over resolved geometry and a clamped-linear
normalization n = min(1, raw/R_worst) with pinned anchors.
- The vacuous-geometry rule (a metric over absent geometry evaluates
to 0.0; the notated-but-unrendered honesty edge is an open
question), all-1.0 default tie-breaking weights, and the per-tier
threshold table — Minimal's uniform 0.90 deliberately fails the
all-worst placeholder, forcing real measurement.
- Pins QualityMetricKind (referenced but never defined by the core
spec) and the registered SolverProfile catalog (Draft selects the
Minimal threshold column; Standard/Publication select Standard).
- QualityFloorApproached fires at 0.8x the applicable threshold and
is status-neutral by requirement.
Reference Suite v0.1.0 (spec/reference_suite.tex, new):
- Six entries referenced by deterministic builder + seed (RS-1
ten_measure_single_staff, RS-2 valid_score_rich, RS-3..6 corpus
fixtures), each with the declared A4-at-8mm-staff solve geometry
(Canvas.layout_defaults has no graph home yet, P12-I7).
- All entries required at Minimal; the same set is the pre-declared
Standard bar (no implementation claims Standard yet). Fixed-
expectation tests deliberately unused in v0.1.
Real metrics in the engraver (engrave/src/quality.rs, new;
layout-ir/src/quality.rs = the catalog constants transcribed):
- QualityMetricVector::unmeasured() replaced with computed values:
collision sweep with the catalog's same-slot-cluster and stroke
exclusions, per-system spacing CV, vertical gap deviations,
system-break slack, page fill, casting-off width CV, symbol
density; slur/beam vacuously 0.0 (no drawn geometry exists).
- Bit-identical across repeated solves (tested); floor warnings never
change solve status; malformed inputs keep unmeasured(). The two
all-worst test pins now assert real values; the StubSolver's
unmeasured() stays (Stub genuinely computes nothing).
Reference-suite harness (testkit reference_suite module + tests):
- Each RS entry asserts the four-condition Minimal pass (hard
constraints, byte/bit determinism, well-formed Minimal report,
every axis within threshold) under the F1 Pass/Xfail discipline,
with the measured table printed per run.
- HONEST FINDING, day one: RS-1 fails Minimal casting-off (measured
1.0 vs 0.90) — greedy first-fit leaves a two-measure stub last
system (width CV 0.6145). Encoded as an asserted Xfail row (fails
on XPASS) and filed as P12-I11 (engrave balance pass, or catalog
revision). P12-I12: the Standard spacing floor warns on short
scores with wide lead measures.
Multi-system click-to-insert fix (editor-core):
- Casting-off exposed two inversion breaks: position_anchors fed a
non-monotonic cross-system anchor list into a monotonic inverter
(system-2 clicks resolved to system-1 times), and
nearest_manifestation found only system 1's staff-line segment
(system-2 clicks got system-1 pitch geometry).
- Fixed with a containing-system lookup over the resolved pages tree
(containment, else nearest by vertical distance), per-system staff
resolution, and per-system anchor filtering; degenerate-geometry
fallback preserves the flat path, so all 84 pre-existing
editor-core tests pass unmodified.
- Five regression tests through the real Engraver over the wrapped
ten-measure fixture, each shown to fail without the fix; testkit
gains dev-only dependencies on editor-core and engrave.
Trackers: P12-I11/I12 filed; DECISIONS entries in engrave, layout-ir,
and testkit; Phase-3 memory updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd