Commit Graph

4 Commits

Author SHA1 Message Date
Levi Neuwirth 029039bf35 Straighten the QMC version story in the docs that assert it
Review follow-up on 5b016f3. Bumping the catalog to 0.3.0, I ran a mechanical
find-and-replace over the version string and did not reread the sentences around
it. Result: layout-ir's quality.rs credited the spacing_distortion refinement to
v0.3.0 when that was v0.2.0, and engrave's quality.rs still announced v0.2.0
while implementing the v0.3.0 unit set.

Each version now says what it actually did: v0.2.0 narrowed spacing_distortion's
measurement DOMAIN to rhythmic columns; v0.3.0 narrowed vertical_density_penalty's
CONTRIBUTING UNITS to one per realization of a gap band. Both narrowed what is
measured over, not what it is normalized against -- so the transcribed constants
(anchors, thresholds, warning fraction) have not moved since v0.1.0, which is the
invariant worth stating and the reason the two revisions were safe.

Swept for the same rot rather than fixing only the two reported. Three more
present-tense claims pinned a version that will keep going stale: engrave and
layout-ir DECISIONS both said "the companion (v0.2.0) ratifies/pins the nine
axes", and the reference suite claimed RS-1 passes "under engrave v3, QMC v0.2.0
anchors" -- engrave is at 11. All three now cite the current version and name what
is actually invariant across revisions. The remaining "QMC 0.1.0 -> 0.2.0"
mentions are historical records of what that revision did and stay as they are.

Docs only. Gate green; zero golden churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:54:25 -04:00
Levi Neuwirth 9d28dd6615 Clear P12-I12: scope spacing_distortion to rhythmic columns (QMC 0.2.0)
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
2026-07-05 13:57:10 -04:00
Levi Neuwirth bc9fb27d36 Clear P12-I11: casting-off widow-rebalance evens the stub last system
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
2026-07-03 19:09:51 -04:00
Levi Neuwirth b9a9921d50 Phase 3 tranche 2: quality conformance companions, real metrics, multi-system click fix
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
2026-07-03 10:39:05 -04:00