gabriel / muse public
crdt-midi-rga.md markdown
82 lines 2.9 KB
Raw
sha256:a73c3f57b665e8c0be2c9e977b3ebefdb7ae8d46f196986d911c6a8f5d8b8d49 docs: update store.py references to focused module paths Sonnet 4.6 28 days ago

Voice-Aware Music RGA

Status

Experimental — not wired into the production merge path. This module (muse/plugins/midi/_crdt_notes.py) exists to:

  1. Demonstrate commutative concurrent note editing.
  2. Benchmark voice-aware RGA vs. LSEQ and standard three-way merge.
  3. Serve as the implementation foundation for a future live collaboration layer.

Why standard RGA is wrong for music

Standard RGA (Roh et al., 2011) orders concurrent insertions at the same position lexicographically by op_id. Two agents inserting a bass note and a soprano note at the same beat would have their pitches interleaved arbitrarily — soprano might appear before bass, producing voice crossings that are musicologically nonsensical.

Music-RGA position key

NotePosition is a NamedTuple with four fields that are compared in order:

NotePosition = (measure, beat_sub, voice_lane, op_id)
Field Purpose
measure 1-indexed bar number
beat_sub Tick offset within the bar
voice_lane 0=bass, 1=tenor, 2=alto, 3=soprano — orders by register
op_id Random hex identifier — tie-break for concurrent edits in the same voice

At the same (measure, beat_sub), notes are ordered by voice lane — bass before treble — preventing voice crossings regardless of insertion order.

CRDT laws

The three lattice laws hold:

  1. Commutativity: merge(a, b).to_sequence() == merge(b, a).to_sequence()
  2. Associativity: merge(merge(a, b), c) == merge(a, merge(b, c))
  3. Idempotency: merge(a, a).to_sequence() == a.to_sequence()

Verified by tests/test_crdt.py.

Tombstone semantics

Deleted entries are tombstoned (marked tombstone=True) rather than removed. This is standard RGA: the tombstone ensures that the deleted entry's position remains stable for other replicas that may have concurrent insertions relative to it. In the join operation, tombstone wins: if either replica has deleted an entry, the merged result considers it deleted.

Voice lane assignment

Automatic voice lane assignment uses a coarse tessiture model:

MIDI pitch range Voice lane Label
0–47 0 Bass
48–59 1 Tenor
60–71 2 Alto
72–127 3 Soprano

Agents performing explicit voice separation can override voice_lane when calling MidiRGA.insert().

Relationship to the commit DAG

At commit time, MidiRGA.to_domain_ops(base_sequence) translates the CRDT state into canonical InsertOp / DeleteOp entries for storage in the commit record. The CRDT state itself is ephemeral — not stored in the object store.

File Role
muse/plugins/midi/_crdt_notes.py NotePosition, RGANoteEntry, MidiRGA
tests/test_crdt.py CRDT law verification + unit tests
tools/benchmark.py RGA throughput benchmark
File History 2 commits
sha256:a73c3f57b665e8c0be2c9e977b3ebefdb7ae8d46f196986d911c6a8f5d8b8d49 docs: update store.py references to focused module paths Sonnet 4.6 28 days ago
sha256:b6cae4448122b2cc690d913be26f7e0a539f11855b8d288bd48be43eb532b5b2 refactor: migrate all source callers off muse.core.store re… Sonnet 4.6 minor 28 days ago