The chosen Phase-3 attack, run as two parallel waves. 829 workspace tests pass; clippy -D warnings, fmt, and rustdoc clean; all three spec documents build with zero undefined references. Casting-off (epiphany-engrave/src/casting.rs, wired into the Engraver): - Greedy first-fit system breaking per region at measure-start barline columns; a measure that would overflow the content width starts a new system. Hard SystemBreakAt/PageBreakAt always honoured; soft breaks honoured unless pathological (skipped with the spec's warning + IrOverride-recorded decision). - Vertical stacking from real content extents with the inter-system gap read from the vertical band model; page overflow starts the next page. World frame: pages stacked vertically, coordinates baked into glyphs/strokes, so the SVG renderer, hit-testing, and the GUI viewport are unchanged. - Real ResolvedPage/ResolvedSystem trees (1-based page numbers, content bounding boxes, staves from staff-line segments, measures from barline columns); every chosen break appends an EngravingDecision with MUSCLOID EngravedBreak provenance, UserOverride-attributed via the new ConstrainedLayoutIR break_origins sidecar; staff lines split per system with synthesized continuation provenance. - Break-constraint evaluation flips: satisfied iff the layout breaks at the slot. The two single-system tests invert deliberately (a hard break is now honoured; a user break is honoured and attributed instead of warned). Geometric constraints evaluate in the pre-casting spaced frame (documented). - Page geometry is engraver-side PageGeometry (A4 portrait at an 8 mm staff: page 105 x 148.5 staff spaces, margins 7.5, content 90 x 133.5; arithmetic documented) — Canvas.layout_defaults has no graph home and is a schema-major addition (P12-I7). ENGRAVER_VERSION = 2. Goldens regenerated: ten_measure_single_staff engraves as 2 systems (viewBox 84x20.6, was 103x11); valid_score_rich as 3 systems; stub goldens byte-identical. K1 schema-fill (Operation Catalog 0.4.0 -> 0.5.0, ratified first; wire discriminants strictly appended): - CreateStaff (24 / tag InsertStaff 24): set-union mint of a global Staff; CreateStaffInstance now preconditions that its referenced staff is live. - SetTimeSignature (25): value-carrying meter-change LWW keyed by (region, resolved position); the carried TimeSignature mints set-union; StructuralFieldCollision on meter_sequence. - SetTempoSegment (26): LWW keyed by (scope, resolved start) over the score or region tempo map; a write that would malform the map refuses with the appended PreconditionFailureReason 11 (TempoMapMalformed). - SetStaffLayout (27): LWW advisory over the staff instance's three inline layout fields. - Create score/canvas remain deliberately unavailable slots: the root and canvas are inline singletons with no addressable object model (P12-K8), not force-designed. Value-restoring undo (P11-C8 narrowed; catalog §UndoTransaction rewritten and per-primitive undo notes updated): - Canonical-order write chains (base-seeded) across all eleven LWW families. StrictInverse restores each written key to its chain-predecessor value iff the transaction's write is still the key's last writer, else refuses the whole undo with a TransactionConflict naming the superseder; BestEffort restores the still-last keys. Clean compensations are Applied; only minted- object tombstone repairs ride AppliedWithRepair (no new repair vocabulary). Mixed mint+overwrite transactions compose; strand guards refuse tombstoning mints still referenced by live non-members. - Undo-of-undo pinned and tested: restorations are chain writes, so undoing the undo's transaction restores the undone value, and a second undo of the same transaction conflicts (absence-restores repeat idempotently — documented asymmetry, P12-K11). - Permutation invariance pinned across five delivery orders; the convergence generators gain the new ops and a tx-then-undo flow. - Still deferred in normative text: delete resurrection (needs a system-derived tag outside the ratified closed set), Transpose inversion (P12-K2), Cascade dependent closure. Trackers: Binary Format companion 0.1.0 -> 0.2.0 (appended wire/tag tables, PreconditionFailureReason 11, payload layouts, history row — a schema-minor evolution under its own rules); nine new Pass-12 rows (C5, K8-K11, I7-I10); core-spec OperationKind listing gains the four kinds; revision-history rows in core spec and companion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NEs4aYiu8MXjdYdMxw8PTd |
||
|---|---|---|
| .. | ||
| 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.