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")
- Spelling algorithm — N/A (Agent H,
epiphany-core). - 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. - Renderer SVG dialect — N/A here (see
epiphany-render-svg/DECISIONS.md). - 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's0. 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-identicalcanonical_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 falseSolved. When constraints are present the scaffold additionally attaches aSolverWarningnaming 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 thexof its spring slot and keeps its baseliney(the vertical pass is future work).
Pass 12 candidates
See spec/PASS12_BATCH.md (rows P12-I1, P12-I2, P12-I3) — all three are now
resolved:
- P12-I1 (resolved by I-1) — the v0 pipeline was a structural placeholder
(one arbitrary glyph per object at
y = 0).to_constrainednow builds real notation (clef-relative noteheads, accidentals, key/time signatures, rests, barlines, stems) and the Engraver re-spaces it; the Ch 7 engraving boundary resolved to notation-construction-in-to_constrained, spacing-in-the-Engraver. - P12-I2 (resolved) — the
MUSCLOIDlayout-object id derivation is wired (epiphany-determinismreserves the built-in tag;layout-irprovenance and the engraving-decision id route through it). - P12-I3 (resolved by I-4a) —
BRAVURA_METRICSis re-extracted from the same SHA-pinnedbravura-1.392font the outlines come from, with bboxes rounded outward to contain the drawn ink (arender-svgtest proves containment).