cgcardona / muse public
feat phase-3 main

feat(phase-3): operation-level merge engine — OT-based auto-merge for non-conflicting ops (#16)

Implements Phase 3 of the supercharge plan: the merge engine now reasons over DomainOp trees rather than file-path sets, enabling sub-file auto-merge.

Core OT engine (muse/core/op_transform.py) - MergeOpsResult dataclass with merged_ops, conflict_ops, is_clean - ops_commute() — commutativity oracle for all 25 DomainOp type pairs - transform() — position-adjusted (a', b') for commuting InsertOp pairs - _adjust_insert_positions() — counting-formula position adjustment for multiple concurrent insertions (correct for any N concurrent inserts) - merge_op_lists() — three-way merge at operation granularity: kept, consensus, and exclusive additions with OT conflict detection - merge_structured() — StructuredDelta convenience wrapper

Domain protocol extension (muse/domain.py) - StructuredMergePlugin sub-protocol (runtime_checkable) with merge_ops() - Updated module docstring to document Phase 3 design

Music plugin (muse/plugins/music/plugin.py + midi_diff.py) - MusicPlugin.merge_ops() — full reference implementation: - OT conflict classification via merge_op_lists - File-level ops (Insert/Delete/Replace) applied directly to manifest - PatchOps: one-side-only → take that side's content hash; both-sides with commuting child ops → MIDI note-level reconstruction - .museattributes strategy resolution for conflicting pairs - _merge_patch_ops() — loads base/ours/theirs MIDI from object store, builds content_id→NoteKey lookups, applies merged note ops, calls reconstruct_midi() and stores result - _note_content_id() — shared hash function for NoteKey identity - reconstruct_midi() in midi_diff.py — Type 0 MIDI writer (inverse of extract_notes), sorts events by tick, handles note_off ordering - isinstance(plugin, StructuredMergePlugin) assertion added

CLI merge command (muse/cli/commands/merge.py) - Phase 3 dispatch: checks isinstance(plugin, StructuredMergePlugin) - Calls diff() for both branches then merge_ops(); falls back to merge() automatically for plugins without structured merge support

Tests - tests/test_op_transform.py — 82 new tests: commutativity oracle (all 25 type pairs), OT transform diamond property, counting formula, merge_op_lists (3-way), merge_structured, MergeOpsResult - tests/test_core_merge_engine.py — 20 new tests: TestMergeStructuredIntegration and TestStructuredMergePluginProtocol classes

Verification: mypy 0 errors, typing_audit 0 violations, 612 tests green

Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>

G Gabriel Cardona <cgcardona@gmail.com> · 2d ago Mar 18, 2026 · 53d2d9ce · parent 986c055e
← Older Oldest commit on main
All commits
Newer → Latest commit on main

Comments

0

No comments yet. Be the first to start the discussion.