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 |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| DECISIONS.md | ||
| README.md | ||
README.md
epiphany-engrave
Agent I's engraving constraint solver (spec Chapter 9): turns a
ConstrainedLayoutIR into a ResolvedLayoutIR with real geometry. It is the
production-side replacement for epiphany-layout-ir's interface-only
StubSolver; the two live in separate crates so the spec's core/product boundary
stays sharp.
Status: Minimal tier, with casting-off
Engraverruns a deterministic horizontal spacing pass (the first axis of the two-pass spring layout): each spring slot is placed left-to-right by a collision-aware advance derived from real glyph bearings.- A casting-off pass (Phase 3's layout track) then breaks the spaced line
into systems at measure boundaries (greedy first-fit against a
PageGeometry— default A4 portrait at an 8 mm staff), stacks systems vertically at the vertical-band model's inter-system gap, assigns pages by content height, and populates the realResolvedPage/ResolvedSystemtree. Every position is baked into a single y-up world frame (pages stacked vertically), so the SVG renderer and hit-testing consume the flat glyph/stroke lists unchanged. - The IR's declared constraints are evaluated — geometric families against
the pre-casting spaced frame, break constraints against the final break
structure (hard breaks are always honoured; a pathological soft break is
skipped and recorded as an
IrOverridedecision). Chosen breaks are recorded asEngravingDecisions withSynthesisKind::EngravedBreaktargets, attributed to the user override that requested them when one did. - It reports
SolverTier::Minimal: hard constraints (break family included) satisfied, no optimality claim — the quality-metric vector stays the honest all-worst placeholder until the Quality Metric Catalog lands.
Deferred: the vertical soft-spring solve, per-system justification/stretch,
optimal break search, widow/orphan control, and casting-off caching. See
DECISIONS.md.
use epiphany_engrave::Engraver;
use epiphany_layout_ir::{ConstraintSolver, SolverConfig};
let report = Engraver::default().solve(&constrained_ir, &SolverConfig::default());
assert!(report.satisfied_hard_constraints);
let resolved = report.layout; // real pages/systems; hand to epiphany-render-svg