epiphany/crates
Levi Neuwirth e199fabe3f Schema major 1 Phase C: Canvas.layout_defaults + real migrate-on-read
The first real data-model field of the schema-major-1 bump, and the point
where the dispatch seam flips from the Phase-B identity to a genuine
migration. Zero golden churn: CanvasLayoutDefaults::default() equals the
engraver's existing A4/8mm default, so no resolved geometry moves.

- Data model (core/graph.rs): Canvas gains `layout_defaults:
  CanvasLayoutDefaults`, with new core geometry primitives CanvasSize /
  CanvasMargins (staff-space CanonicalF64, A4/8mm Default -- core has no
  geometry types of its own and must not depend on layout-ir). Exported from
  the crate root; ~7 Canvas construction sites updated with
  `..Default::default()`.
- Codec (core/codec.rs): struct_codec! for the three new types; Canvas v1
  encodes `regions` then `layout_defaults`.
- The frozen migrate-on-read (decode_v0_score): a byte-level splice. v0 Score
  bytes are the v1 layout minus Canvas.layout_defaults; Canvas is Score field 2
  and its v0 layout was just `regions`. Read the v0 prefix (metadata,
  canvas.regions) to find the split, insert the default CanvasLayoutDefaults
  encoding, then decode the resulting v1 bytes. Total and default-filling (no
  score context), frozen by value. A `pos()` accessor was added to Reader for
  the splice.
- Test (v0_score_migrates_by_default_filling_layout_defaults): derives REAL v0
  bytes by stripping the field from a v1 encoding, then migrates them back and
  checks the original score is reconstructed with the default refilled. A wrong
  splice offset corrupts the bytes and fails the decode, so it guards the
  frozen v0 assumptions.

Deferred: routing the migrate through a bundle acceleration_snapshots slot (a
Phase-B Finding-3 item). The migrate logic is proven by the core unit test and
the bundle read path by the canonical-base roundtrip; combining them hits
cross-crate friction (constructing v0 bytes needs core-internal Reader, which
testkit can't reach and core-below-bundle can't stage) for marginal coverage.
The forward scenario (v1 reader migrating a v0 bundle's major-0 acceleration
snapshot) needs no new bundle code -- the v0 snapshot passes the exact gate.

863 workspace tests pass; clippy -D warnings, fmt --check, rustdoc -D warnings
all clean; no render/snapshot goldens changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd
2026-07-05 20:37:36 -04:00
..
epiphany-bundle Schema major 1 Phase B: the dispatch seam + version infrastructure (no-op) 2026-07-05 19:21:11 -04:00
epiphany-core Schema major 1 Phase C: Canvas.layout_defaults + real migrate-on-read 2026-07-05 20:37:36 -04:00
epiphany-determinism P12-I2: wire the ratified MUSCLOID layout-object id derivation 2026-06-27 12:46:19 -04:00
epiphany-editor-core Phase 3 tranche 2: quality conformance companions, real metrics, multi-system click fix 2026-07-03 10:39:05 -04:00
epiphany-editor-gui Phase 3 tranche 1: casting-off, K1 schema-fill, value-restoring undo 2026-07-02 21:55:26 -04:00
epiphany-engrave Schema major 1 Phase A: ratify the v1 wire form + migration (spec only) 2026-07-05 18:05:38 -04:00
epiphany-layout-ir Clear P12-I12: scope spacing_distortion to rhythmic columns (QMC 0.2.0) 2026-07-05 13:57:10 -04:00
epiphany-ops Phase 3 tranche 1: casting-off, K1 schema-fill, value-restoring undo 2026-07-02 21:55:26 -04:00
epiphany-render-svg Clear P12-I11: casting-off widow-rebalance evens the stub last system 2026-07-03 19:09:51 -04:00
epiphany-testkit Schema major 1 Phase C: Canvas.layout_defaults + real migrate-on-read 2026-07-05 20:37:36 -04:00