Score::empty plus operations alone now materializes a note-bearing Score. The chain CreateInstrument -> CreateStaff -> CreateRegion -> CreateStaffInstance -> CreateVoice -> InsertEvent needed exactly one new link: CreateStaff already demanded a live Instrument and nothing could create one. Instrument is a root with no outbound references, so the operation carries no referential preconditions -- only mint and byte-identical re-carry, on the CreateStaff template. It designs no wire layout: Instrument joins canonical_value! and the payload is one push_lp_bytes over the existing Codec, so strict canonical-form rejection is inherited rather than written. Kind 31 and tag 31 agree; schema_major is unconditionally 2 (Instrument's major-2 appends are mandatory, not Option-hidden); bundle.rs is untouched and the op-block accept-set stays 2, since that raise belongs to G2. Two cross-cutting items the ruling required. Reduction now writes identity for the first time, deriving next_counter from the log rather than trusting the seed -- and the implementation is broader than contracted, covering minted entity ids as well as operation ids, which is right: both burn counters. And the from-empty path is pinned to reduce_operation_set_onto, since the base-free mode skips referential preconditions by design; a test documents that asymmetry as designed rather than as a bug to fix. The contract's parallel-safety claim was WRONG and this commit corrects it. Extending OperationKind is not containable to core+ops: Rust exhaustiveness forces an arm in editor-core's barriers.rs, and because testkit depends on editor-core, that one missing arm blocked conformance and requirement_labels too. Three more downstream sites had 31 or a kind-count baked in as a literal -- layout-ir's barrier decode test, testkit's grammar vocabulary count, and the textproj corpus generator. The subagent found the first two, reverted its out-of-bounds edit, and reported rather than working around; the user authorized the boundary crossing. Each literal now carries a comment saying it must move with every tag append. The text projection needed a companion bump, which the contract never anticipated. Adding create-instrument to the kind production while holding 0.7.0 would leave two incompatible grammars claiming one version -- precisely what the single-version gate exists to prevent -- so COMPANION_VERSION is now 0.8.0, the first kind appended since the header was gated. Cached projections do not migrate and are not expected to: a TextProjection chunk is a non-canonical accelerator, so a stale one is regenerated. The negative "wrong version" vector had to flip, since 0.8.0 was the version it used as its future-and-therefore- rejected example; it now names 0.7.0, which tests the deferred migrate-on-read posture better anyway. Test headers that were literals now assert against the constant. Gate, all observed: fmt clean; clippy --workspace --all-targets 0 warnings; 1359 passed / 0 failed; requirement_labels 6/6; conformance 8/8 and 9/9 with golden-gate, 96 decode vectors and 13 textproj vectors, every verdict agreed. max_supported_major(OperationEnvelopeBlock) verified still 2. Both PDFs rebuilt. Mutations i1, i3 and i5 re-run independently rather than taken on report: the spine collapses to TargetMissing without the instrument, an unseeded instrument_values misreports a base re-carry as RecreateContentMismatch, and a seed-returning cursor yields 0 where 12 is required. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QjsEnYhm1gPpf6ii2iFxFV |
||
|---|---|---|
| .. | ||
| examples | ||
| src | ||
| tests | ||
| Cargo.toml | ||
| DECISIONS.md | ||
| README.md | ||
README.md
epiphany-ops
The Epiphany concurrent semantics: the operations through which the score
graph becomes a live model, and the deterministic reduction by which a set of
operations becomes a materialized score state. Implements the normative
requirements of Chapter 6 (Semantic Operations and Concurrent Reduction) of
the core specification (spec/core_spec.pdf). This is Agent C's crate per
spec/QUICKSTART.md, building on Agent A's epiphany-determinism and Agent B's
epiphany-core.
A score's state is defined by the set of operations committed to it. Any materialized graph is a deterministic reduction of that set; caches, snapshots, and partial reductions are acceleration structures, never the source of truth. — Chapter 6, Design Principles
The thesis in one paragraph
The replicated operation set is a grow-only CRDT: replicas accumulate envelopes and converge on the same set. The materialized graph is not a CRDT — it is the deterministic reduction of that set in a single canonical order (causal-first, then the HLC tuple). Any permutation of the same envelopes reduces to byte-identical materialized state. That property is the determinism heart of the architecture, and the reduction fuzzer is its tripwire.
What's here
| Area | Items | Spec |
|---|---|---|
| Stamps | HybridLogicalClock, OperationStamp, the reduction & monotonicity tuples |
Ch. 6 §"Operation Identity and Stamps" |
| Causal context | CausalContext (dotted version vector), covers, the missing-predecessor signal |
Ch. 6 §6.2 |
| Payloads | OperationKind, the discriminator-only OperationKindTag, OperationPayload, the §6.10 representative ops |
Ch. 6 §"Operation Envelopes", §6.10 |
| Envelopes | OperationEnvelope, EnvelopeHash (MUSCENVH), well_formed (incl. stamp.id == id) |
Ch. 6 §6.4 |
| Slots | OperationSlot::{Single, Equivocated}, the order-independent (Pass-10) transitions |
Ch. 6 §6.5 |
| Anomalies | AnomalousReplicaSegment, IntegrityAnomaly/Kind, the HLC-monotonicity detector |
Ch. 6 §6.6; Ch. 5 §"System-Derived Counter Collisions" |
| Effects | OperationEffect, NoOpReason, the typed PreconditionFailureReason, RepairRecord/RepairKind |
Ch. 6 §6.3.2, §6.5 |
| Conflicts | ConflictRecord, ConflictKind, content-derived ConflictId (derive_conflict_id), the registry, resolution |
Ch. 6 §6.4 |
| Transactions / undo | TransactionDescriptor with the causal-prior-descriptor rule, UndoTransactionPayload / UndoPolicy |
Ch. 6 §6.6, §6.8 |
| Operation set | OperationSet: accept pipeline (well-formedness → slot → causal), grow-only |
Ch. 6 §"Envelope Acceptance" |
| Reduction | canonical_reduction_order (single function), MaterializedState, the reduction driver |
Ch. 6 §6.3 |
The determinism this crate enforces
- A single reduction-order function.
canonical_reduction_orderperforms deterministic causal topological ordering, using the intrinsic stamp tuple(physical, logical, replica, counter)only among ready operations. - Order-independent equivocation. A duplicate
OperationIdwith different canonical bytes transitions its slot toEquivocatedregardless of which envelope arrived first (Pass 10). Equivocated slots contribute nothing to reduction; dependents are held pending. - Content-derived facts.
ConflictIdandIntegrityAnomalyIdare derived from content, so two replicas reducing the same set agree on every conflict and anomaly id — the conflict registry and anomaly register are deterministic materialized facts, not local bookkeeping. - Byte-identical materialized state.
MaterializedState::canonical_bytesserializes the effect log, conflict registry, anomaly register, object existence, spellings, and LWW fields in their normative orders. - Real graph materialization.
OperationSet::reduce_onto(&base_score)returnsGraphMaterialization { state, score }. The graph is mutated in the same canonical order and compares by canonical event identity, independent of arena storage order.
Hand-off gates
Run the gate harnesses (QUICKSTART, Agent C):
cargo test -p epiphany-ops
cargo run --release -p epiphany-ops --example fuzz_reduction # 10k iters, seed 0
cargo run --release -p epiphany-ops --example fuzz_reduction 100000 7 # soak, seed 7
- Reduction determinism — every randomized envelope set reduces to byte-identical materialized state under any acceptance order (v0 acceptance criteria 1 and 5).
- Equivocation order-independence — every duplicate-id-with-different-bytes scenario equivocates regardless of arrival order (v0 acceptance criterion 3).
The integration tests (tests/concurrent_reduction.rs) exercise these plus
transaction atomicity, descriptor precedence, anomaly exclusion, and forward
undo through the public API.
Scope and decisions
Chapter 6 specifies the framework and a representative selection of
operations; the full ~60–80-primitive catalog is an explicit open question
(§6.11) deferred to the Operation Catalog companion. This crate implements the
framework in full and the representative operations, which is sufficient to
exercise every reduction discipline. The representative operations can also
reduce onto an epiphany_core::Score: insert/delete, voice promotion, supported
cross-cutting structures, system breaks, migration checks, transaction
rollback, and undo mutate the real graph while preserving Agent B's invariants.
reduce() remains the base-free CRDT/bookkeeping API; reduce_onto() is the
graph-aware editing path. See DECISIONS.md for remaining payload boundaries.
Per QUICKSTART "Don't do these": undo is the spec's forward compensating
operation, never inverse-based; unsafe is forbidden; everything is sync.