epiphany/crates/epiphany-layout-ir
Levi Neuwirth ff9bd0fd06 G-minor: the chunk schema minor becomes a derived record
Implements the schema-minor MUST (binary_format.tex, Schema Versioning) that no
writer had ever honoured: a writer raises the chunk minor when it emits a
discriminant appended after the minor it otherwise declares, so an
unknown-discriminant decode failure is attributable to version skew rather than
corruption.

introduced_minor() lands on the five vocabularies with post-baseline variants -
OperationKind, OperationKindTag, OperationPayload, ReanchorReason, and
PreconditionFailureReason - each exhaustive with no wildcard arm, so a future
variant cannot compile without being assigned an epoch. The tag epochs live
inside operation_kind_tag_vocabulary! rather than beside it, because a sibling
match is the parallel list that macro exists to prevent. The sentinel is
Option<u16>, not 0, since 0 is a real baseline minor for V1-V3 and conflating
them would make the max read correctly only by accident.

An envelope's required minor is the max over every discriminant it actually
emits; a block's is the max over its envelopes; major and minor derive
independently. Baselines are not normalised - V0 keeps minor 1.

The manifest seam keeps epiphany-bundle opaque: no ops or layout-ir dependency,
and the aggregate version is supplied by the producer rather than derived, with
CommitContext carrying the previous one so unchanged barrier content preserves
it. The version rides the superblock slot that already exists; Manifest gains no
field, which would have been schema-major and would have defeated the rung.
bundle.rs's superblock check stays major-only - tightening it to full-version
equality is a conformance regression, and s11 locks that.

textproj carries the manifest SchemaVersion and never derives it, so
COMPANION_VERSION moves 0.9.0 to 0.10.0 with the corpus regenerated. Not because
of op-block stamping, which remains projection-invisible. A new normative
requirement records the carry-never-derive rule in the companion itself; its
rationale names layout-ir, which textproj genuinely lacks, rather than the
operation vocabulary, which it has.

Gate [7f] adds an independent oracle over decodable in-tree barrier fixtures,
requiring exact equality rather than >=. Equality is load-bearing: >= catches
under-stamping but not stale over-stamping after the sole maximum contributor is
removed. Undecodable blobs are reported not-checkable, never as a pass.

Also repairs binary_format.tex's stale claim that OperationKind and
OperationKindTag append at 30 with a history stopping at 29, while 30-33 are
taken and the normative tables already carry them.

Gate: 1399 tests, clippy 0, fmt clean, conformance 8/8 with [7f] at four
fixtures checked and one not-checkable, 102 decode vectors byte-identical
(verified, not assumed), 13 text-projection vectors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV
2026-07-28 21:04:34 -04:00
..
src G-minor: the chunk schema minor becomes a derived record 2026-07-28 21:04:34 -04:00
Cargo.toml Land epiphany-layout-ir (Agent E): layout IR + solver interface 2026-06-19 19:58:01 -04:00
DECISIONS.md Editor T4-pre W1: the resolved layout stops discarding its own partition 2026-07-24 14:47:31 -04:00
README.md Land epiphany-layout-ir (Agent E): layout IR + solver interface 2026-06-19 19:58:01 -04:00

README.md

epiphany-layout-ir

The Epiphany layout intermediate representation and constraint-solver interface, implementing the normative requirements of Chapter 7 ("Layout Intermediate Representation") and the interface of Chapter 9 ("Constraint-Solver Interface") of the core specification (spec/core_spec.pdf). This is Agent E's crate per spec/QUICKSTART.md — it lands last among the implementation crates, building on Agent A's epiphany-determinism and Agent B's epiphany-core (and on Agent C's OperationKindTag for the edit-barrier types — see DECISIONS.md).

The IR sits between the score graph and two downstream consumers: the constraint solver, which resolves spacing and positioning, and the renderer, which produces final visual output. — Chapter 7

The score graph is the canonical truth about the music; this crate is a downstream projection of it. The transformation is a pipeline of four stages, each with its own type and a deterministic, provenance-preserving contract for the next.

What's here

Area Items Spec
Stage 1 — logical LogicalLayoutIR, LayoutRegion, composite LayoutObject, overrides/cross-region objects, to_logical Ch. 7 §"LogicalLayoutIR"
Stage 2 — constrained ConstrainedLayoutIR, SpringSlot, LayoutConstraint, GlyphObject, to_constrained Ch. 7 §"ConstrainedLayoutIR"
Stage 3 — resolved ResolvedLayoutIR, pages/systems/staves/measures, ResolvedGlyph Ch. 7 §"ResolvedLayoutIR"
Stage 4 — render (interface only) RenderIR, RenderPrimitive, RenderIRProducer, to_render Ch. 7 §"RenderIR"
Time axis canonical TimeAxisModel plus dynamic TimeAxis, including registered payload preservation Ch. 7 §"Layout Regions"
Provenance Provenance, LayoutObjectId, SynthesisKind, stable_layout_id (a pure function of the source — stable across relayouts) Ch. 7 §"Provenance"
Engraving decisions EngravingDecision/EngravingDecisionId/EngravingDecisionKind, DecisionSource Ch. 7 §"Engraving Decisions"
Vertical bands VerticalBand/VerticalBandId/VerticalBandKind Ch. 7 §"Vertical Bands"
Incremental cache LayoutCache, DependencyIndex, granular stage caches and invalidation Ch. 7 §"Incremental Layout and Caching"
Glyph catalog GlyphCatalog (metric-lookup interface) + in-tree BravuraCatalog, GlyphCatalogIdentity, SmuflVersion, FontId, GlyphMetric/GlyphAnchor, BRAVURA_METRICS/BRAVURA_VERSION, metrics_hash_for (MUSCFNTM-tagged) Ch. 7 §"Glyph Catalog Interface" / §7.3.2
Edit barriers EditBarrier, BarrierScope, BarrierCondition, ObjectKind, EditContext (precise scope evaluation), keyed on Agent C's OperationKindTag Ch. 8 §"Edit Barriers"
Solver interface ConstraintSolver (solve/solve_incremental, Send + Sync), SolverConfig/SolverBudget, SolverState, InvalidationSet, SolveReport, SolveStatus, SolverTier/SolverVersion, the v0 StubSolver Ch. 9
Round-trip round_trip, RoundTripReport, laid_out_object_ids Ch. 7 (v0 acceptance criterion 6)

The stub solver

Per the QUICKSTART, the v0 constraint solver is a stub: StubSolver returns SolveStatus::Solved with the input geometry verbatim (each glyph's resolved position is exactly its constrained baseline), preserves provenance, and reports all hard constraints satisfied. The real solver — Cassowary or otherwise — comes later (Chapter 9 specifies the interface, not the algorithm); v0 only needs to round-trip IR through the solver interface to prove the contracts hold. The one stub validates the full Chapter 7 §7.3.2 catalog identity and all slot, band, and geometry cross-references before reporting Solved. Explicit constraints are rejected because this interface-only solver cannot honestly evaluate them.

The round-trip (v0 acceptance criterion 6)

round_trip runs graph → LogicalLayoutIRConstrainedLayoutIR → stub-solved ResolvedLayoutIRRenderIR and asserts the contract every stage must satisfy:

  • the stub solver reports Solved with all hard constraints satisfied;
  • the complete Provenance of every object — source, synthesis, dependencies, and stable_id — survives every stage unchanged;
  • no two objects ever share a stable_id, so manifestation multiplicity is preserved (a source manifested in two regions stays two layout objects);
  • the stub solver returns the input geometry verbatim;
  • the set of score-graph sources recovered from the RenderIR is exactly the set laid out — a surjection onto graph identity (one source may back several manifestations, each with its own stable id).

Agent F's testkit drives this same entry point (layout_stub::round_trip) on the 10-measure single-staff hand-off fixture and the rich multi-region generator.

Algorithmic scope

Per the QUICKSTART ("a prototype baseline, not the product"), this crate implements the Chapter 7 IR contracts and interface types, not a production engraving engine. The real spacing and casting-off algorithms, quality-metric computation, constraint solver, and renderer remain later implementations of these interfaces.

Determinism

IR coordinates are single-precision staff spaces (StaffSpace(f32), Chapter 7 §7.2); the canonical ResolvedLayoutIR output quantizes them to the 1/1024 grid at serialization time (ResolvedLayoutIR::canonical_bytes), exactly as Appendix D §"Quantized Layout Coordinates" prescribes — quantization absorbs all f32 variation below 1/2048 staff space, so the canonical output is independent of the floating-point environment. The glyph-catalog identity hashes its consulted metrics (advance, bounding box, named anchors) under the MUSCFNTM domain tag, and the edit-barrier types carry a canonical encoding with set-valued fields emitted in canonical byte order (sorted and de-duplicated). See DECISIONS.md.

Tests

cargo test -p epiphany-layout-ir covers each module plus the round-trip on Agent B's valid_score / valid_score_rich generators. The end-to-end v0 acceptance gate (criterion 6) lives in the testkit.