gabriel / muse public
fix BREAKING task/fix-phantom-conflicts #2 / 2
AI Agent gabriel · 95 days ago · Jun 11, 2026 · Diff

fix: convergent-edit phantom conflicts in ops_commute + merge debug instrumentation

ops_commute returned False for two ReplaceOps or AddressedInsertOps at the same address regardless of whether their content matched. When both branches independently made the same change to a symbol (convergent edit), this caused phantom conflicts in muse merge and muse pull even though ours and theirs agreed on the final object_id.

Fix: for replace+replace and addressed-insert+insert at the same address, check content equality (new_content_id / content_id). Equal content commutes.

Added MUSE_MERGE_DEBUG=1 instrumentation writing JSON lines to /tmp/muse_merge_debug.log across merge.py, pull.py, plugin.merge, and merge_ops to capture manifests, op lists, and conflict paths for debugging.

Closes #85 (phantom conflicts on convergent symbol edits).

sha256:39065bc65b1a541916c4ea32ccd53eac38bb93015db2be2e342326064e86c44f sha
+25 ~6 symbols
sha256:113c8c70c54f4baca28b073b051f79057bfdd9a1936a7e34630c9052e20cd196 snapshot
+25
symbols added
~6
symbols modified
0
dead code introduced
Semantic Changes 31 symbols
~ muse/core/merge_debug.py .py 10 symbols added
+ _ENABLED variable variable _ENABLED L21–21
+ _LOG_PATH variable variable _LOG_PATH L22–22
+ Any import import Any L19–19
+ annotations import import annotations L13–13
+ json import import json L15–15
+ os import import os L16–16
+ pathlib import import pathlib L17–17
+ time import import time L18–18
+ merge_debug_log function function merge_debug_log L25–38
+ merge_debug_manifest_summary function function merge_debug_manifest_summary L41–50
~ muse/cli/commands/merge.py .py 2 symbols added, 1 symbol modified
+ merge_debug_log import import merge_debug_log L107–107
+ merge_debug_manifest_summary import import merge_debug_manifest_summary L107–107
~ muse/cli/commands/pull.py .py 2 symbols added, 1 symbol modified
+ merge_debug_log import import merge_debug_log L85–85
+ merge_debug_manifest_summary import import merge_debug_manifest_summary L85–85
~ run
~ muse/core/op_merge.py .py 1 symbol modified
~ muse/plugins/code/plugin.py .py 1 symbol added, 3 symbols modified
+ merge_debug_log import import merge_debug_log L90–90
~ merge
~ tests/test_op_merge.py .py 10 symbols added
+ TestOpsCommuteConvergentEdits class class TestOpsCommuteConvergentEdits L717–784
+ test_addressed_insert_same_address_different_content_conflicts method method test_addressed_insert_same_address_different_content_conflicts L755–759
+ test_addressed_insert_same_address_same_content_commutes method method test_addressed_insert_same_address_same_content_commutes L749–753
+ test_patch_convergent_addressed_insert_child_commutes method method test_patch_convergent_addressed_insert_child_commutes L779–784
+ test_patch_convergent_replace_child_commutes method method test_patch_convergent_replace_child_commutes L763–770
+ test_patch_divergent_replace_child_conflicts method method test_patch_divergent_replace_child_conflicts L772–777
+ test_replace_different_addresses_always_commute method method test_replace_different_addresses_always_commute L736–739
+ test_replace_same_address_different_new_content_conflicts method method test_replace_same_address_different_new_content_conflicts L729–734
+ test_replace_same_address_old_content_ignored_for_convergence method method test_replace_same_address_old_content_ignored_for_convergence L741–745
+ test_replace_same_address_same_new_content_commutes method method test_replace_same_address_same_new_content_commutes L722–727

0 comments

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

To add a comment, use the Muse CLI: muse hub commit comment sha256:39065bc65b1a541916c4ea32ccd53eac38bb93015db2be2e342326064e86c44f --body "your comment"