Land the two real pre-passes as canonical *derived annotations* (pure functions of the materialized Score + profile, recomputed on materialization, never serialized into canonical Score bytes), exposed via `derive_annotations`: - Spelling: a Temperley-style line-of-fifths centre-of-gravity preference rule (key-free, deterministic), preserving authored CMN letters and only inferring spelling for chromatic/integer input. `resolve_spelling` layers authored overrides above the inferred default (the RespellPitch precedence rule). `spell` now takes `&Pitch` and delegates to `simplest_spelling`. - Decomposition: metric greedy-aligned splitting on a 1/4096 integer grid (barline + dyadic-boundary ties), with exact sounding->notated tuplet conversion before gridding. Components reconstruct the event duration (invariant 15). - A per-event-kind eligibility `TaxonomyReport` so "ineligible" is always explicit and counted, never silently absent. Test infrastructure (Agent F): a 29-fixture representative corpus + taxonomy harness (corpus.rs), the H spelling/decomposition merge gate (prepass_harness.rs), a discrete `tests/prepass.rs` CI target, conformance stage [7b], a dedicated CI job, and the Pass-12 batch tracker. Review hardening folded in (nine findings): - Guard `decompose_metric` against a zero-length measure (was a divide-by-zero panic; now reported ungriddable). - Resolve spelling-override priority via `Reverse` instead of negation (was an i32::MIN overflow). - Verify spelling *register* (octave), not just pitch class, in the gate. - Close the decomposition under-emission gap: the unusual-outcome taxonomy buckets are an exact per-fixture whitelist (classify_corpus step 5b). - Generalize `accidental_ids` to a glyph stack so authored extreme alterations (triple-sharp+) reconstruct exactly instead of being clamped. - Per-fixture spread checks in the non-vacuity tripwire and broad-bucket coverage, so no single rich fixture can carry a signal (partial-stub resistance); added a `mixed_rhythm` fixture for margin. - Pin the integer-grid note-value math to the canonical rational helpers via an exhaustive test; cross-reference comments. - Replace the O(n^2) tuplet innermost-resolution scan with an id index. fmt + clippy -D warnings clean; 199 tests pass; conformance suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011giSRaHCFCGm1Z2SWv6JHt |
||
|---|---|---|
| .. | ||
| examples | ||
| src | ||
| tests | ||
| Cargo.toml | ||
| DECISIONS.md | ||
| README.md | ||
README.md
epiphany-testkit
Agent F's crate per spec/QUICKSTART.md: the
cross-cutting conformance testkit. It is the architecture's tripwire — the suite
that proves the other crates work end to end and that runs in CI (see
.github/workflows/ci.yml).
It provides:
- Deterministic property-test generators for the public types of A
(
epiphany-determinism), B (epiphany-core), C (epiphany-ops), D (epiphany-bundle), and E (epiphany-layout-ir). Agent B's score-graph generators/shrinkers are re-exported asgenerators::graph. - The canonical round-trip harness (
roundtrip) — v0 acceptance criterion 4 (typed values + bundle container; the bookkeepingMaterializedStateround-trip is retained asassert_reduction_serialization_stable). - The CRDT convergence harness (
convergence) — criteria 1 and 5. Criterion 1 proper is real-Score convergence throughreduce_onto(run_graph_convergence); the byte-canonical bookkeeping-projection convergence (assert_convergence) backs criterion 5. - The equivocation harness (
equivocation) — criterion 3. - The crash-recovery harness (
bundle_harness) — Agent D's gate, criterion 2. - The manifest-selection harness (
bundle_harness). - The layout round-trip harness (
layout_stub) — criterion 6. - The audit regression guards (
negative) — one guard per defect the Agent C framework audit surfaced (the M1 fixes), so a regression trips this suite directly.
All harnesses are real
The QUICKSTART charters Agent F to "build against A and stubs for the others."
All five implementation crates — A, B, C, D, and now E (epiphany-layout-ir) —
have shipped, so every harness drives the real crate.
| Harness | Backend | Status |
|---|---|---|
roundtrip (criterion 4) |
A + B + C + D, real | real |
bundle_harness (criterion 2, manifest selection) |
D, real | real |
convergence (criteria 1, 5) |
C (epiphany-ops), real |
real |
equivocation (criterion 3) |
C (epiphany-ops), real |
real |
layout_stub (criterion 6) |
E (epiphany-layout-ir), real |
real |
For criteria 1, 3, and 5 the testkit drives the real
epiphany_ops::OperationSet / canonical_reduction_order / reduce and also
re-exports Agent C's own authoritative gates
(convergence::ops_reduction_determinism_fuzz,
equivocation::ops_equivocation_fuzz). The layout_stub module — once a
faithful in-tree stub of Chapters 7 & 9 — now re-exports the real
epiphany-layout-ir IR types and stub solver behind the same round_trip
signature; the provenance-preservation contract is implemented and tested inside
that crate. (The "stub" in the module name now refers to the spec-sanctioned
stub constraint solver, not to a stubbed crate.)
Criterion 1: real-Score vs. reducer-bookkeeping convergence
Criterion 1 proper (convergence::run_graph_convergence, the acceptance
criterion_1_convergence test) is real-Score convergence: a real ~50-bar,
two-voice base epiphany_core::Score is edited by two replicas through
OperationSet::reduce_onto, and the entire materialized graph — arena, voices,
tombstones, cross-cutting, and the bookkeeping state — must be identical
under every delivery order, pass check_invariants, and genuinely grow both
edited voices (non-vacuity). The session targets the base's actual voice ids
(generators::graph_edit_session), so it exercises the integration point, not a
synthetic id space.
The earlier, narrower gate is retained and honestly renamed
(reducer_bookkeeping_convergence): it converges the byte-canonical
bookkeeping projection (OperationSet::reduce →
MaterializedState::canonical_bytes) — the Chapter 6 §6.3 ledger (effects,
conflicts, anomalies, tombstones, spellings, pending), not the full musical
graph. It still backs criterion 5 and proves causal-first ordering
(convergence::assert_causal_order_respected,
run_authoritative_reduction_gate). The bookkeeping two-staff scenario remains
instantiated — a real ~50-bar (TWO_STAFF_BARS) session whose staves are
asserted populated by generators::assert_two_staff_populated, not just modeled.
Criterion 4: what is and isn't tested
Criterion 4 has three tiers — two asserted now, one pending item 5:
-
Real decode round-trips (these catch decoder / canonicalization defects): the generic
CanonicalEncode/CanonicalDecodeproperty swept across every typed identifier, bothRationalTimearms, and everyTypedObjectIddiscriminant; the bundleManifest(encode → decode → encodefixpoint, with a rich generator exercising snapshots, blobs, extensions, varied profiles, retention, and the optional roots); theFixedHeader; and theSuperblockslot encoding. Crucially, the decoders are shown to validate: corrupting a manifest or header makesdecodereject it (assert_manifest_decode_rejects_corruption,assert_header_decode_rejects_corruption). -
A reducer-bookkeeping serialization tier (
reducer_bookkeeping_serialization, viaassert_reduction_serialization_stable): a realOperationSetis reduced to itsMaterializedState::canonical_bytes()— the canonical bookkeeping state, not the whole musicalScore— which is stored as aSnapshotchunk referenced by the manifest'scanonical_base, survives the bundle's content-addressed store (hash-verified on reopen), decodes throughMaterializedState::decode_canonical, compares structurally with the original reduction, and re-serializes byte-identically. The decoder validates nested tags, lengths, primitive values, canonical form, and trailing bytes. Musical sensitivity is proven two ways:assert_content_mutation_changes_serialization(a cloned operation set with identical ids/stamps/causal contexts but one changed payload reduces to different bytes — the rebuttal to an id-only serializer) andassert_distinct_scores_serialize_differently. The materialized realScoreitself is shown reproducible today (full_score_materialization_is_reproducible, structural equality across delivery orders) — the determinism precondition a byte codec depends on. -
The full-
Scorebyte round-trip (criterion_4_full_score_byte_roundtrip, viaassert_score_serialization_stable): item 5's whole-score codec (epiphany_core::Score::canonical_bytes/decode_canonical) has landed, so a real ~50-barScore— materialized through Agent C'sreduce_onto— nowencode → decode → re-encodes byte-identically through a real bundle snapshot (hash-verified on reopen), with the decodedScorestructurally equal to the original. This is the whole musical graph (arena, voices, regions, cross-cutting, tombstones), not the bookkeeping projection.
Decisions (per QUICKSTART "Make each one once and document it")
- No platform entropy in the harness. Appendix D §"Randomness" forbids
platform entropy in canonical state; the testkit holds itself to the stronger
rule that no platform entropy enters the harness at all. Everything draws
from
rng::Rng(a wrapper over Agent A's vendored SplitMix64, with unbiased bounded draws and an overflow-safe full-rangerange), so every failure reproduces from its seed. - Drive the real crate once it ships; stub only what hasn't landed. Earlier
in development
epiphany-ops(C) andepiphany-layout-ir(E) were in-flight and their harnesses ran against faithful in-tree stubs; now that both have shipped, every harness drives the real crate and re-exports its gates.
Flagged for a future spec pass (Pass 11 candidates)
Per the QUICKSTART, implementation-discovered gaps are batched, not improvised:
- Whole-graph (
epiphany_core::Score) wire format — landed (item 5). A direct canonical byte codec for the coreScorenow exists (epiphany_core::Score::canonical_bytes/decode_canonical), andcriterion_4_full_score_byte_roundtripexercises it on a realreduce_ontomaterialization through a bundle snapshot. The prototype byte form predates the Binary Format companion specification and is to be reconciled with it (seeepiphany-core/DECISIONS.md, P11-4). - Layout harness re-pointed.
epiphany-layout-irhas landed, solayout_stubnow drives the real IR types behind the sameround_tripsignature (done). IR coordinates are f32 staff spaces, quantized only when serializing canonicalResolvedLayoutIR(Appendix D); see that crate'sDECISIONS.mdfor the remaining layout-specific Pass 11 candidates (theOperationKindTagvariant set and the layout-object id derivation).
Running
# Unit + acceptance tests (a meaningful slice, under the cargo test timeout):
cargo test -p epiphany-testkit
# The full conformance suite at scale, outside the test timeout (includes Agent
# A's 1,000,000-iteration determinism gate and Agent C's reduction/equivocation
# gates):
cargo run --release -p epiphany-testkit --example conformance_suite # scale 1
cargo run --release -p epiphany-testkit --example conformance_suite 10 # soak
cargo run --release -p epiphany-testkit --example conformance_suite 0 # smoke
The six v0 acceptance criteria are asserted in tests/acceptance.rs, one test
per architecture layer.