# epiphany-core The Epiphany **score graph**: the in-memory representation of all musical content in a score, implementing the normative requirements of **Chapters 2–5** of the core specification (`spec/core_spec.pdf`). This is Agent B's crate per `spec/QUICKSTART.md` — the largest scope — building only on Agent A's `epiphany-determinism`. > The graph is the canonical truth about the music; layout, serialization, and > editing operations are downstream projections and consumers of it. > — Chapter 5, Design Principles ## What's here | Area | Items | Spec | |------|-------|------| | Identifiers | `ReplicaId` (+ `SYSTEM_DERIVED`), `OperationId`, the typed 128-bit family (`EventId`, `PitchId`, `VoiceId`, …), `TypedObjectId`, `IdentityContext`, `derive_system_id` | Ch. 5 §"Identifiers"; Ch. 6 §"Operation Identity" | | Time | `RationalTime` (inline-or-promoted), `MusicalPosition`/`MusicalDuration` (typed algebra), `WallClockTime`/`WallClockDuration`, `TimeAnchor`/`AnchorOffset`, `EventPosition`/`EventDuration`/`ConcreteDuration`, `TimeSignature`/`BeatGroup`, `NotatedComponent`/`NoteValue` | Ch. 3 | | Tempo | `TempoMap`, `Tempo`, `TempoSegment`, `TempoShape` with closed-form `musical_to_wallclock`/`wallclock_to_musical` over constant/linear/exponential segments (curve deferred → `TempoError`) | Ch. 3 §"Tempo Map" | | Pitch | `Pitch`, `ScalePosition`, `IdentifiedPitch`, `PitchSpelling`, the spelling-attachment subsystem, `ReferencePitch`, `spell` (single-pitch simplest spelling), all three equivalences (`scale_position_equivalent`, `enharmonic_equivalent`, `sounding_equivalent`) | Ch. 2; Ch. 4 registry ids | | Pre-passes | `derive_annotations`: the real spelling pre-pass (Temperley line-of-fifths) + notational-decomposition pre-pass (metric greedy-aligned splitting) as canonical *derived annotations* (not stored), the eligibility `TaxonomyReport`, and `resolve_spelling` (authored-override precedence) | Ch. 2 §"Spelling Pre-Pass"; Ch. 3 §"Notational Decomposition" | | Events | the `Event` taxonomy (7 variants) and the `slotmap`-backed `EventArena` | Ch. 5 §"The Event Arena" | | Graph | `Canvas`, `Region`, `Staff` vs `StaffInstance`, `Voice`/`VoiceOrigin`, `Measure`, `BarlineAlignmentGroup`, aleatoric `EventOrderingDAG` (acyclic by construction), the full cross-cutting registry, the full top-level `Score` | Ch. 5 | | Indexes | `ScoreIndexes`: the four mandatory indexes (event-time, cross-cutting-reference, measure, spelling-attachment) | Ch. 5 §"Indexes" | | Invariants | `check_invariants` over all 19 enumerated graph invariants, with a typed `InvariantViolation` witness per check | Ch. 5 §"Graph Invariants" | | Generators | `generators::valid_score`/`valid_score_rich` (positive), `violating_score` (negative, per invariant), `shrink` (witness minimizer) | QUICKSTART, Agent B hand-off | ## The identity discipline this crate enforces 1. **Replica + counter, big-endian canonical bytes.** Every typed identifier is `(replica << 64) | counter`; its canonical 16-byte form is `to_be_bytes()` (8-byte replica, 8-byte counter) and the numeric `Ord` *is* the Appendix-D lexicographic byte order. Identity is exact, never tolerant. 2. **A reserved system namespace.** `ReplicaId::SYSTEM_DERIVED` is rejected by `ReplicaId::generate`/`from_entropy`; system-derived ids (`derive_system_id`, `derive_promoted_voice_id`) live only in that namespace, with counters `trunc64(BLAKE3(domain_tag || canonical_inputs))` via `epiphany-determinism`. 3. **Cross-kind confusion is a compile error.** Each object kind has its own newtype; `TypedObjectId` tags them apart with a discriminant that is part of canonical content. ## Graph invariants as property tests The Chapter 5 invariants are **property tests in CI, not runtime assertions in release builds** (QUICKSTART). `check_invariants` returns every violation with a small witness. For each invariant `generators` provides: - a **positive generator** (`valid_score` / `arbitrary_graph_corpus`) whose output passes every check, and - a **negative generator** (`violating_score`) plus a **shrinker** (`shrink`) that minimizes a violating graph to a small witness while retaining only the structure the violation needs. Generation is deterministic (a vendored SplitMix64), so a failing case reproduces from its seed — no platform entropy enters generation (Appendix D §"Randomness"). ## Implementation decisions Per QUICKSTART "Decisions you'll need to make" (full rationale in `DECISIONS.md`): - **Replica entropy: `getrandom`** (decision 1). `ReplicaId::generate` re-draws until the value is not the reserved namespace. - **Event-arena storage: `slotmap`** (decision 2) plus a hash index for the required `O(1)` `EventId` lookup and generation-checked stale handles. - **Sync only** (decision 4): no async anywhere. - **Current stable Rust** (decision 5); MSRV pinned at the workspace's 1.77. - `RationalTime`'s promoted arm uses `num-rational`'s `BigRational`, the spec's reference design (Ch. 3 §"Recommended Implementation"). - `unsafe` is forbidden crate-wide (`#![forbid(unsafe_code)]`). ## Hand-off criteria (QUICKSTART, Agent B) - [x] Every invariant has both a generator and a shrinker (`generators::{valid_score, violating_score, shrink}`; one per invariant, property-tested), plus targeted tests for the cross-cutting/anchor/tie sub-rules. - [x] The arbitrary-graph corpus runs clean (`generators::tests::positive_corpus_runs_clean`, 500 graphs), and a breadth corpus (`valid_score_rich`: concurrent metric/proportional/ aleatoric regions, measures, triplet, tie, spanner, marker, chord symbol, decomposition, tombstones) runs clean over 200 seeds. - [x] `cargo test -p epiphany-core` clean (69 unit + 5 integration). - [x] `cargo clippy --all-targets -- -D warnings` clean; `cargo doc` clean under `RUSTDOCFLAGS="-D warnings"`. ### Depth of the invariant checks The checks are not surface-level. In particular: invariant 3 computes per-clock event intervals and detects both disorder and overlap; invariant 7 resolves region extents to absolute wall-clock coordinates (wall-clock leaves, plus event/region/measure-start anchors — an event anchor is its region origin plus its region-relative position) and only skips pairs that can't be placed without the deferred tempo map; invariant 9 sweeps *every* reachable anchor (region extents, meter changes, measure starts, clef/key changes, user breaks, spanners, spelling ranges); invariant 10 resolves *all* graph references — cross-cutting anchor targets, annotation layers, tuplet parents, graphic objects, and event-internal references (indeterminate alternatives, trajectory event-pitches, graphic-event objects, cue sources); invariant 11 covers every id kind, plus tombstone/live collisions, `SYSTEM_DERIVED` misuse (including the score's own identity context), and arena index/well-formedness integrity (catching post-`get_mut` corruption); invariant 17 validates explicit *and* implicit (pitch-id-ascending) tie pairings, per-class adjacency, and the cross-voice position rule; invariant 18 recomputes the deterministic promoted-voice derivation. Enharmonic equivalence is a sounding notion (octave matters: C4 ≠ C5). Empty pitched events are rejected at the arena boundary and re-checked; `IdentityContext::try_new` rejects the reserved replica and counters use `checked_add` so a counter is never silently reused. ### Known bounded limitations (deferred dependencies) - **Tempo conversion** integrates the piecewise map in closed form for `Constant`/`Linear`/`Exponential` segments (Chapter 3 §"Conversion"); only `TempoShape::Curve` is deferred to the open numerical algorithm (`TempoError::CurveIntegrationUnsupported`). Segment boundaries that cannot be placed without the score graph, and malformed segment sequences, return a `TempoError`, never a wrong answer. The inverse round-trips ordinary rhythms via a documented continued-fraction approximation (DECISIONS P11-7). - **Region time-overlap (invariant 7)** resolves extents to absolute wall-clock, now including musical event positions placed through the region's effective tempo map (its `local_tempo_map`, else the score map). Extents that still cannot be placed (no tempo defined, or a deferred curve) are skipped rather than rejected. Sound (no false positives), incomplete (DECISIONS P11-4). - **System-promoted voice derivation (invariant 18)** retains the winning and losing operation ids on `VoiceOrigin::SystemPromoted`; the checker recomputes the exact four-input derivation used by `epiphany-ops`. - **The Chapter 4 tuning *catalog*** — `PitchSpace`/`TuningSystem`/ `AccidentalRegistry` *definitions*, the built-in catalog, the hierarchical resolver, and the position→frequency resolution function — is **not** an Agent B deliverable (the QUICKSTART lists those as referenced-by-id). This crate models the identifiers and the score-level `ScoreTuningContext`; `Pitch::sounding_equivalent` takes a caller-supplied frequency resolver. See DECISIONS P11-5. ## Scope boundaries The full Chapter 5 top-level `Score` shape is modeled (metadata, instruments, staff groups, parts, tuning context, tempo map, analysis layers, views) along with the complete `CrossCuttingRegistry` (slurs, ties, beams, tuplets, spanners, markers, repeats, analytical annotations, comments, graphic gestures, lyrics, chord symbols). The reference- and identity-bearing fields are modeled in depth; deeper *bodies* (tuning resolution, tempo-curve integration, part layout, view recipes, glyph/engraving detail) are Chapters 3/4/7 and later companions. Engraving-display detail (`StemConfiguration`, `ClefChange`, `KeySignatureChange`, articulations, dynamics, line styles, spanner/marker visual kinds) is *introduced informally here and fully defined in Chapter 7* — it belongs to Agent E (`epiphany-layout-ir`), and this crate carries minimal, clearly-marked placeholders for it. Operation envelopes, stamps (HLC), causal contexts, the canonical reduction, tombstone *tracking*, and conflict records are Chapter 6 / Agent C (`epiphany-ops`); `epiphany-core` defines only the `OperationId` they hang off and the tombstone-aware invariants. Ambiguities discovered while building are **not** resolved in code — they are batched as Pass 11 candidates in `DECISIONS.md` (QUICKSTART, Process notes).