//! The six v0 acceptance criteria (QUICKSTART §"v0 acceptance criteria"), //! driven only through the testkit's public surface. Each test corresponds to //! one architecture layer; if any fails, that layer is not done. The heavy soak //! versions (1M / 10k iterations) live in `examples/conformance_suite.rs`; these //! run a meaningful slice under the `cargo test` timeout. //! //! All six criteria run against the real shipped crates (A/B/C/D/E): criterion //! 6 drives the real `epiphany-layout-ir` through the `layout_stub` harness //! module (Agent E has landed). See the crate docs for the harness policy. use epiphany_testkit::{ bundle_harness, convergence, equivocation, fixtures, generators, layout_stub, migration, negative, roundtrip, Rng, }; /// Criterion 1 — **Convergence (real Score).** Overlapping edits to a real /// ~50-bar, two-voice base [`epiphany_core::Score`] by two replicas converge to /// an *identical materialized Score* — the real graph (arena, voices, /// tombstones, cross-cutting) together with its bookkeeping state — regardless /// of envelope delivery order, with every Chapter 5 invariant intact. Driven /// through Agent C's `OperationSet::reduce_onto`. This is the headline criterion; /// [`reducer_bookkeeping_convergence`] is its ledger-projection counterpart. #[test] fn criterion_1_convergence() { for seed in 0..24u64 { convergence::run_graph_convergence(6, seed.wrapping_mul(0x9E37_79B9).wrapping_add(11)); } } /// **Reducer-bookkeeping convergence** (the former, weaker half of criterion 1, /// retained and honestly renamed). The canonical Chapter 6 §6.3 *bookkeeping /// projection* — `OperationSet::reduce` → `MaterializedState::canonical_bytes` — /// is byte-identical across delivery orders. That projection is the ledger /// (effects, conflicts, anomalies, tombstones, spellings, pending), **not** the /// full musical graph; real-Score convergence is `criterion_1_convergence`. /// Kept as a fast byte-level determinism gate (and the basis of criterion 5). #[test] fn reducer_bookkeeping_convergence() { // The named shape: a two-staff, overlapping-edit session (bookkeeping bytes). for seed in 0..16u64 { convergence::run_two_staff_convergence(8, seed.wrapping_mul(0x9E37_79B9).wrapping_add(11)); } // Plus a broader sweep of smaller randomized sessions. for seed in 0..200u64 { convergence::run_convergence(24, 6, seed.wrapping_mul(0x9E37_79B9)); } } /// **Audit regression guards.** Every defect the Agent C framework audit /// surfaced (the M1 fixes) is independently re-asserted here so a regression in /// `epiphany-ops` trips Agent F's suite directly. See [`negative`]. #[test] fn audit_defect_regressions() { negative::run_all(); } /// Criterion 2 — **Crash safety.** A crash between any two syscalls in the /// commit path leaves the bundle openable — possibly at the previous /// generation, never corrupt (Chapter 8's atomic commit). Runs the testkit's /// randomized driver, an exhaustive per-syscall sweep, and Agent D's own gate. #[test] fn criterion_2_crash_safety() { bundle_harness::run_crash_recovery(2_000, 0xF00D_BEEF_1234_5678); let mut rng = Rng::new(0x00A1_1CE5); for base_commits in 0..3u64 { let (image, generation) = bundle_harness::build_base(&mut rng, base_commits); bundle_harness::exhaustive_crash_sweep( &image, generation, &[b"alpha".to_vec(), vec![9u8; 300]], ); } bundle_harness::bundle_crash_recovery_fuzz(2_000, 0x0123_4567_89AB_CDEF); } /// Criterion 3 — **Equivocation.** An injected duplicate `OperationId` with /// different canonical bytes produces an `OperationSlot::Equivocated` at both /// replicas, regardless of arrival order (Pass 10's order-independence fix). #[test] fn criterion_3_equivocation() { for seed in 0..300u64 { equivocation::run_equivocation(16, seed.wrapping_mul(0x9E37_79B9).wrapping_add(3)); } // Agent C's authoritative equivocation gate (a slice). equivocation::ops_equivocation_fuzz(2_000, 0x1234_5678); } /// Criterion 4 — **Canonical serialization stability (typed + container).** The /// same canonical value serialized → loaded → re-serialized produces /// byte-identical bytes (Appendix D's canonical-serialization layer): the /// type-level round-trip corpus over every `CanonicalEncode` type in Agents A /// and B, Agent A's determinism gate (a slice), and the real bundle /// manifest/header — including decoder rejection of corruption. /// /// The **full-Score** byte round-trip is split out below: its bookkeeping /// projection ([`reducer_bookkeeping_serialization`]), its reproducibility /// ([`full_score_materialization_is_reproducible`]), and — now that item 5's /// whole-`Score` codec has landed — the real byte-level round-trip /// ([`criterion_4_full_score_byte_roundtrip`]). #[test] fn criterion_4_canonical_serialization_stability() { roundtrip::run_roundtrip_corpus(100_000, 0x00C0_FFEE_1234_5678); // A slice of Agent A's 1,000,000-iteration determinism gate (the full run // lives in the conformance suite). roundtrip::run_determinism_roundtrip_gate(200_000, 0x0A11_CE5E_EDED_2024); for seed in 0..48u64 { roundtrip::assert_manifest_roundtrip(&roundtrip::committed_manifest( seed.wrapping_mul(0x9E37_79B9).wrapping_add(5), )); let mut rng = Rng::new(seed.wrapping_mul(0x0100_0193).wrapping_add(17)); let rich = generators::rich_manifest(&mut rng); roundtrip::assert_manifest_roundtrip(&rich); // The decoder actually validates: corruption is rejected. roundtrip::assert_manifest_decode_rejects_corruption(&rich); } } /// **Reducer-bookkeeping serialization** (the former, narrower half of criterion /// 4, retained and honestly renamed). The canonical *bookkeeping projection* /// (`MaterializedState::canonical_bytes`) survives content-addressed storage in /// a real bundle and re-serializes byte-identically, and is musically sensitive /// (same identities + changed content → different bytes). This is the Chapter 6 /// ledger, **not** the whole musical `Score`; the full-Score byte round-trip is /// [`criterion_4_full_score_byte_roundtrip`] (pending item 5). #[test] fn reducer_bookkeeping_serialization() { for seed in 0..48u64 { let mut rng = Rng::new(seed.wrapping_mul(0x0100_0193).wrapping_add(17)); // The reduced canonical state survives content-addressed storage, and is // musically sensitive: same identities + changed content → different bytes. let session = generators::operation_envelopes(&mut rng, 40, 3, 6, 6); roundtrip::assert_reduction_serialization_stable(&session, seed); let other = generators::operation_envelopes(&mut rng, 41, 3, 6, 6); roundtrip::assert_distinct_scores_serialize_differently(&session, &other); } // Strong sensitivity: same identities, changed content -> different bytes. roundtrip::assert_content_mutation_changes_serialization(); } /// **Full-Score materialization reproducibility** (achievable without the byte /// codec). Reducing the same edit session onto the same base `Score` twice — /// once in authored order, once shuffled — yields the *identical* materialized /// `epiphany_core::Score` and bookkeeping state. This is the determinism /// precondition any future whole-Score byte codec depends on, asserted today via /// structural equality of the real graph (`reduce_onto`). #[test] fn full_score_materialization_is_reproducible() { for seed in 0..16u64 { convergence::run_graph_convergence(4, seed.wrapping_mul(0x0100_0193).wrapping_add(23)); } } /// Criterion 4 (full-Score byte round-trip) — **the whole-graph tier (item 5).** /// A real ~50-bar `epiphany_core::Score`, materialized through Agent C's /// `reduce_onto`, is `encode → decode → re-encode`d byte-identically through the /// whole-score canonical codec and a real bundle snapshot (hash-verified on /// reopen). This is the honest full-Score serialization gate the bookkeeping /// projection ([`reducer_bookkeeping_serialization`]) only approximated. #[test] fn criterion_4_full_score_byte_roundtrip() { for seed in 0..24u64 { let seed = seed.wrapping_mul(0x9E37_79B9).wrapping_add(13); let (score, frontier) = convergence::materialized_score(seed); roundtrip::assert_score_serialization_stable(&score, &frontier, seed); } } /// **Operation-block summaries (Chapter 8, C/D integration).** An ops-computed /// block summary (causal frontier + min/max operation stamp) survives a real /// bundle commit + reopen and is selectable by block id without decoding the /// block payload. #[test] fn operation_block_summaries_survive_storage() { let mut rng = Rng::new(0x0B5_5044_0B0B_0B0B); for seed in 0..16u64 { let envelopes = generators::operation_envelopes(&mut rng, 24, 3, 8, 8); roundtrip::assert_operation_block_summary_survives_storage( &envelopes, seed.wrapping_add(1), ); } } /// Criterion 5 — **Reduction determinism.** A randomized 1,000-envelope set, /// reduced 10 times in 10 different orders, produces byte-identical materialized /// states *and* an identical canonical reduction order (Appendix D's /// canonical-reduction layer). Drives the real reducer. #[test] fn criterion_5_reduction_determinism() { let mut rng = Rng::new(0x5EED_0005_0F0F_0F0F); let envelopes = generators::operation_envelopes(&mut rng, 1_000, 3, 40, 40); // The big set honors causal order and reduces deterministically. convergence::assert_causal_order_respected(&envelopes); let order = convergence::assert_reduction_determinism(&envelopes, 10, &mut rng); assert!(!order.is_empty()); // The testkit's authoritative gate over many conformant sets (proves // causal-order correctness, not just permutation invariance). convergence::run_authoritative_reduction_gate(1_500, 3, 0x00C0_FFEE_0042); // Agent C's own hand-off gate, including conformant causal histories and // explicit anomaly injections. convergence::ops_reduction_determinism_fuzz(2_000, 0x00C0_FFEE); } /// Criterion 6 — **Layout round-trip.** A score graph → LogicalLayoutIR → /// stub-solved ResolvedLayoutIR → RenderIR interface call completes without /// panic and without losing provenance back-references (Chapter 7's IR /// contract). Driven on the 10-measure single-staff hand-off fixture and the /// rich multi-region generator. #[test] fn criterion_6_layout_round_trip() { for seed in 0..128u64 { let report = layout_stub::round_trip(&fixtures::ten_measure_single_staff(seed)); assert!(report.glyphs > 0); // The render IR carries glyph *and* stroke primitives; the round-trip // recovers a source for every one of them. assert_eq!( report.render_primitives, report.glyphs + report.render_strokes ); layout_stub::round_trip(&generators::graph::valid_score_rich(seed)); } } /// The manifest-selection harness (QUICKSTART, Agent F): every corruption /// scenario plus the commit-protocol selection check. #[test] fn manifest_selection_harness() { for seed in 0..16u64 { bundle_harness::run_manifest_selection(seed); } } /// **Agent K — Operation Catalog v0→v1 migration.** The shift from /// identifier-only payloads to value-typed payloads ships a one-time migration; /// this is its merge gate (QUICKSTART Agent K acceptance: deterministic and /// equivalence-preserving migration). For each random corpus, the v1 envelopes /// project to their v0 wire shape and migrate back, reducing to byte-identical /// canonical state — and the gate's non-vacuity guard proves a wrong migration /// would be caught. See [`migration`]. #[test] fn agent_k_migration_equivalence_gate() { migration::assert_migration_gate_is_not_vacuous(); for seed in 0..32u64 { migration::run_migration_equivalence(48, seed.wrapping_mul(0x9E37_79B9).wrapping_add(5)); } }