epiphany/crates/epiphany-engrave/DECISIONS.md

5.0 KiB

epiphany-engrave — decisions and Pass 12 candidates

This file records (a) the implementation decisions the Phase-2 QUICKSTART asked Agent I to make once and document, and (b) ambiguities discovered while building the crate, batched as Pass 12 candidates (spec/PASS12_BATCH.md) rather than improvised in code.

Scope and phase status

epiphany-engrave is the production-side constraint solver (Chapter 9): it turns a ConstrainedLayoutIR into a ResolvedLayoutIR with real geometry. It is a separate crate from epiphany-layout-ir deliberately — layout-ir is the interface layer (the graph↔renderer contract); the actual constraint-solving is on the product side of the spec's core/product boundary, so replacing the StubSolver inside layout-ir would blur it (spec/PHASE2_QUICKSTART.md, crate topology).

This phase ships the renderer-against-stub slice (QUICKSTART, Agent I, "Development pattern": build the renderer against the stub solver first, then grow the real solver). So this crate is an honest scaffold: Engraver runs a genuine deterministic horizontal spacing pass — the first axis of the planned two-pass spring layout — placing each spring slot left-to-right by its preferred width, rather than echoing the stub's input columns. It does not yet run the vertical pass, the soft-spring stretch/compress solve, or evaluate the IR's declared hard constraints.

Honest tier

By the same rule layout-ir's StubSolver follows, a solver that does not evaluate the declared hard constraints and computes no quality metrics MUST report SolverTier::Stub, never Minimal (Chapter 9 §"Conformance Tiers"). Engraver::tier() therefore reports Stub today; it is promoted to Minimal in the same change that lands real hard-constraint satisfaction. A regression test (reports_the_honest_stub_tier_until_it_earns_minimal) guards this so the tier cannot be silently inflated. The quality-metric vector stays the conservative all-worst placeholder (QualityMetricVector::unmeasured) until the Quality Metric Catalog lands (Phase 3, explicitly out of Agent I's scope).

Implementation decisions (QUICKSTART "Decisions you'll need to make")

  1. Spelling algorithm — N/A (Agent H, epiphany-core).
  2. Solver architecture for engraving — two-pass spring layout (horizontal then vertical), constraint graph derived from the existing ConstrainedLayoutIR. This is the QUICKSTART's recommendation: it matches the IR's spring-slot / vertical-band shape, and the spec's deterministic-output requirement makes a global optimization solver expensive to validate (hard to make bit-reproducible) and a rule-based fallback brittle. The horizontal pass implemented here (spacing::slot_positions) is that architecture's first axis. Global optimization and rule-based fallback are rejected.
  3. Renderer SVG dialect — N/A here (see epiphany-render-svg/DECISIONS.md).
  4. Catalog versioning / 5. Binary Format versioning — N/A (Agents K, J).

Local decisions

  • #![forbid(unsafe_code)]; sync only; MSRV = workspace 1.77. Same as every implementation crate.
  • Solver version 1 (ENGRAVER_VERSION), distinct from the stub's 0. Chapter 9: within a fixed version, identical input produces identical output. The horizontal pass is a pure function of the slot sequence and preferred widths, so this holds; a determinism test asserts byte-identical canonical_bytes() across solves.
  • Well-formedness gate mirrors the stub. An invalid structure, an unknown glyph, a forged catalog identity, or an explicit hard constraint this scaffold cannot yet evaluate yields SolveStatus::InternalError (diagnostic-only), never a panic and never a false Solved. When constraints are present the scaffold additionally attaches a SolverWarning naming the limitation, rather than silently ignoring them.
  • Horizontal spacing preserves provenance, glyph identity, bounds, style, and layer; it changes only position. It assigns each glyph the x of its spring slot and keeps its baseline y (the vertical pass is future work).

Pass 12 candidates

See spec/PASS12_BATCH.md (rows P12-I1, P12-I2, P12-I3). In brief:

  • P12-I1 — the v0 to_logical/to_constrained pipeline is a structural placeholder (each layout object → one arbitrary glyph by discriminant % N, laid out at y = 0), not real notation. Chapter 7 says the logical stage has "engraving decisions made"; the spec should clarify which engraving decisions (glyph-by-duration selection, pitch→staff-position, clef/key/meter/barline realization, stems/beams) are core-IR construction versus solver work, so the real-notation engraving has a defined home before it is built next phase.
  • P12-I3layout-ir's bundled BRAVURA_METRICS are approximations and disagree with the genuine Bravura outlines the renderer now bundles (e.g. timeSig4 vertical registration). Real spacing needs exact metrics; the metrics table should be regenerated from the font or reconciled with the outline source.