gabriel / muse public
feat BREAKING task/cohen-transform #1 / 1
gabriel · 168 days ago · Apr 13, 2026 · Diff

feat: Cohen Transform — muse checkout -m and muse diff --conflict

Named in honour of Bram Cohen (creator of BitTorrent, Manyana CRDT weave), whose conflict-presentation insight is the direct inspiration.

New: muse/core/cohen_transform.py - Three-way line merge with sync-region algorithm (same approach as Bazaar merge3) - classify_action(): 'inserted' / 'deleted' / 'modified' - annotate_hunk_action(): rewrites @@ headers with [side: action] labels - compute_regions(): decomposes three sequences into MergeRegion objects - three_way_merge_lines(): full diff3-style merge with Cohen action labels - format_conflict_diff(): two-sided labeled diff for conflict review (base→ours and base→theirs, each hunk annotated with its action)

New: muse checkout -m / --merge - Carries uncommitted working-tree changes across a branch switch via three-way merge (base=HEAD, ours=workdir, theirs=target branch) - Clean merges written silently; conflicts written with diff3 + action labels - Binary files detected and left at target version with conflict record - MERGE_STATE.json written on conflict (same format as muse merge, so muse checkout --ours/--theirs works identically for resolution)

New: muse diff --conflict - Reads MERGE_STATE, renders format_conflict_diff() for each conflict path - Per-hunk action annotations show what each side did at a glance - JSON mode: {status: 'conflict', conflicts: [{path, ours_diff, theirs_diff}]} - --path filter supported; exits non-zero when conflicts remain

Fix: agent-config regex in run_set() - Old: r"\[agent-config\][^\[]*" — broke on [ inside array values - New: r"^\[agent-config\].*?(?=^\[|\Z)" with DOTALL|MULTILINE

New tests: 131 tests across 3 files (89 unit + 42 integration) - test_core_cohen_transform.py: unit/integration/stress/data-integrity/security - test_cmd_checkout.py: TestCheckoutMerge* — parser, clean, conflict, errors - test_cmd_diff.py: TestDiffConflict* — parser, no-merge, output, security - test_core_snapshot.py: TestNestedRepoWalk — 16 boundary tests - test_cmd_agent_config.py: expanded to 75 tests covering all set() paths

sha256:2087dbb987c55a1ca7d5f8a956f9cef32356c609c98f18a48eb2f13b0c95e7ab sha
+266 ~26 symbols
sha256:57e89f87044e4c8581eb6daacfa41a8d483d0289dc6bd0d2c324faa8d9e18c05 snapshot
+266
symbols added
~26
symbols modified
0
dead code introduced
Semantic Changes 292 symbols
~ muse/core/cohen_transform.py .py 19 symbols added
+ CONFLICT_SEPARATOR variable variable CONFLICT_SEPARATOR L66–66
+ MergeRegion class class MergeRegion L80–98
+ _MARKER_BASE variable variable _MARKER_BASE L71–71
+ _MARKER_END variable variable _MARKER_END L73–73
+ _MARKER_OURS_PREFIX variable variable _MARKER_OURS_PREFIX L70–70
+ _MARKER_SEP_PREFIX variable variable _MARKER_SEP_PREFIX L72–72
+ _find_sync_regions function function _find_sync_regions L104–167
+ annotate_hunk_action function function annotate_hunk_action L278–344
+ classify_action class function classify_action L260–275
+ compute_regions function function compute_regions L170–254
+ format_conflict_diff function function format_conflict_diff L438–592
+ Callable import import Callable L61–61
+ Sequence import import Sequence L61–61
+ annotations import import annotations L56–56
+ dataclass class import dataclass L60–60
+ difflib import import difflib L58–58
+ field import import field L60–60
+ pathlib import import pathlib L59–59
+ three_way_merge_lines function function three_way_merge_lines L350–432
~ tests/test_core_cohen_transform.py .py 120 symbols added
+ TestAnnotateHunkAction class class TestAnnotateHunkAction L92–148
+ _make_hunk method method _make_hunk L93–97
+ test_empty_hunk_list_returns_empty method method test_empty_hunk_list_returns_empty L135–136
+ test_header_lines_preserved method method test_header_lines_preserved L117–121
+ test_mixed_hunk_labelled_modified method method test_mixed_hunk_labelled_ L111–115
+ test_multiple_hunks_each_annotated method method test_multiple_hunks_each_annotated L123–133
+ test_no_at_markers_unchanged method method test_no_at_markers_unchanged L138–141
+ test_pure_delete_hunk_labelled_deleted method method test_pure_delete_hunk_labelled_deleted L105–109
+ test_pure_insert_hunk_labelled_inserted method method test_pure_insert_hunk_labelled_inserted L99–103
+ test_side_label_appears_in_annotation method method test_side_label_appears_in_annotation L143–148
+ TestClassifyAction class class TestClassifyAction L68–85
+ test_deleted_single_line method method test_deleted_single_line L84–85
+ test_deleted_when_other_empty method method test_deleted_when_other_empty L72–73
+ test_inserted_single_line method method test_inserted_single_line L81–82
+ test_inserted_when_base_empty method method test_inserted_when_base_empty L69–70
+ test_modified_when_both_non_empty method method test_modified_when_both_non_empty L75–76
+ test_modified_when_multi_line_both method method test_modified_when_multi_line_both L78–79
+ TestComputeRegions class class TestComputeRegions L155–226
+ test_all_stable_no_changes method method test_all_stable_no_changes L156–160
+ test_both_same method method test_both_same L178–184
+ test_conflict method method test_conflict L186–192
+ test_empty_sequences method method test_empty_sequences L194–196
+ test_ours_only method method test_ours_only L162–168
+ test_pure_insertion_conflict method method test_pure_insertion_conflict L220–226
+ test_pure_insertion_ours method method test_pure_insertion_ours L212–218
+ test_region_lines_are_lists method method test_region_lines_are_lists L198–204
+ test_stable_region_all_three_equal method method test_stable_region_all_three_equal L206–210
+ test_theirs_only method method test_theirs_only L170–176
+ TestConflictMarkerFormat class class TestConflictMarkerFormat L395–437
+ _conflict_merged method method _conflict_merged L397–402
+ test_base_section_marker_present method method test_base_section_marker_present L416–418
+ test_deleted_action_on_ours_marker method method test_deleted_action_on_ours_marker L404–406
+ test_end_conflict_marker_present method method test_end_conflict_marker_present L420–422
+ test_inserted_action_on_theirs_marker method method test_inserted_action_on_theirs_marker L408–410
+ test_marker_ordering method method test_marker_ordering L424–437
+ test_modified_action_on_modified_conflict method method test_modified_action_on_modified_conflict L412–414
+ TestDataIntegrity class class TestDataIntegrity L771–825
+ test_clean_merge_is_deterministic method method test_clean_merge_is_deterministic L772–779
+ test_conflict_markers_are_balanced method method test_conflict_markers_are_balanced L816–825
+ test_conflict_output_is_deterministic method method test_conflict_output_is_deterministic L781–787
+ test_input_sequences_not_mutated method method test_input_sequences_not_mutated L789–799
+ test_merged_content_contains_all_clean_changes method method test_merged_content_contains_all_clean_changes L801–814
+ TestEdgeCases class class TestEdgeCases L540–605
+ test_all_empty method method test_all_empty L541–544
+ test_base_empty_both_add_different_conflicts method method test_base_empty_both_add_different_conflicts L561–563
+ test_base_empty_both_add_same method method test_base_empty_both_add_same L556–559
+ test_base_empty_ours_adds method method test_base_empty_ours_adds L546–549
+ test_base_empty_theirs_adds method method test_base_empty_theirs_adds L551–554
+ test_both_delete_everything method method test_both_delete_everything L571–575
+ test_lines_without_trailing_newline method method test_lines_without_trailing_newline L599–605
+ test_long_lines_no_crash method method test_long_lines_no_crash L585–591
+ test_many_identical_lines method method test_many_identical_lines L593–597
+ test_ours_equals_base_theirs_deletes method method test_ours_equals_base_theirs_deletes L565–569
+ test_unicode_content method method test_unicode_content L577–583
+ TestFormatConflictDiff class class TestFormatConflictDiff L444–533
+ _make_manifests method method _make_manifests L447–464
+ test_ansi_sanitized_in_path method method test_ansi_sanitized_in_path L506–515
+ test_custom_labels_appear method method test_custom_labels_appear L488–496
+ test_missing_file_shows_no_changes_message method method test_missing_file_shows_no_changes_message L517–524
+ test_no_color_mode method method test_no_color_mode L526–533
+ test_output_contains_conflict_header method method test_output_contains_conflict_header L466–472
+ test_output_contains_ours_section method method test_output_contains_ours_section L474–479
+ test_output_contains_theirs_section method method test_output_contains_theirs_section L481–486
+ test_returns_list_of_strings method method test_returns_list_of_strings L498–504
+ TestIntegration class class TestIntegration L612–703
+ test_append_only_change_ours method method test_append_only_change_ours L682–687
+ test_both_fix_same_typo method method test_both_fix_same_typo L638–644
+ test_conflict_preserves_stable_context_above_and_below method method test_conflict_preserves_stable_context_above_and_below L672–680
+ test_ours_adds_docstring_theirs_renames_param method method test_ours_adds_docstring_theirs_renames_param L615–636
+ test_ours_deletes_block_theirs_modifies_different_block method method test_ours_deletes_block_theirs_modifies_different_block L646–670
+ test_prepend_only_change_theirs method method test_prepend_only_change_theirs L689–694
+ test_roundtrip_clean_merge_deterministic method method test_roundtrip_clean_merge_deterministic L696–703
+ TestSecurity class class TestSecurity L832–889
+ test_ansi_in_content_does_not_spoof_markers method method test_ansi_in_content_does_not_spoof_markers L833–845
+ test_format_conflict_diff_ansi_in_path_sanitized method method test_format_conflict_diff_ansi_in_path_sanitized L879–889
+ test_label_injection_via_newline method method test_label_injection_via_newline L866–877
+ test_null_bytes_in_content_handled method method test_null_bytes_in_content_handled L847–853
+ test_very_long_label_no_overflow method method test_very_long_label_no_overflow L855–864
+ TestStress class class TestStress L710–764
+ test_concurrent_merges_consistent method method test_concurrent_merges_consistent L742–764
+ test_large_file_clean_merge_fast method method test_large_file_clean_merge_fast L711–726
+ test_many_conflicts_no_crash method method test_many_conflicts_no_crash L728–740
+ TestThreeWayMergeLines class class TestThreeWayMergeLines L233–388
+ test_both_empty_insertion_at_same_point_conflicts method method test_both_empty_insertion_at_same_point_conflicts L369–374
+ test_both_same_change_applied_once method method test_both_same_change_applied_once L257–262
+ test_conflict_base_content_in_diff3_section method method test_conflict_base_content_in_diff3_section L345–350
+ test_conflict_detected method method test_conflict_detected L296–301
+ test_conflict_has_base_marker method method test_conflict_has_base_marker L310–315
+ test_conflict_has_end_marker method method test_conflict_has_end_marker L324–329
+ test_conflict_has_ours_marker method method test_conflict_has_ours_marker L303–308
+ test_conflict_has_sep_marker method method test_conflict_has_sep_marker L317–322
+ test_conflict_ours_content_present method method test_conflict_ours_content_present L331–336
+ test_conflict_theirs_content_present method method test_conflict_theirs_content_present L338–343
+ test_custom_labels_appear_in_markers method method test_custom_labels_appear_in_markers L376–388
+ test_empty_base_both_add method method test_empty_base_both_add L287–292
+ test_multiple_independent_conflicts method method test_multiple_independent_conflicts L361–367
+ test_no_changes_returns_base method method test_no_changes_returns_base L237–241
+ test_non_overlapping_changes_both_applied method method test_non_overlapping_changes_both_applied L264–271
+ test_ours_inserts_line method method test_ours_inserts_line L273–278
+ test_ours_only_change_applied method method test_ours_only_change_applied L243–248
+ test_stable_lines_outside_conflict_preserved method method test_stable_lines_outside_conflict_preserved L352–359
+ test_theirs_deletes_line method method test_theirs_deletes_line L280–285
+ test_theirs_only_change_applied method method test_theirs_only_change_applied L250–255
+ _has_marker function function _has_marker L60–61
+ _lines function function _lines L46–48
+ _merge function function _merge L51–57
+ CONFLICT_SEPARATOR import import CONFLICT_SEPARATOR L31–31
+ MagicMock import import MagicMock L27–27
+ MergeRegion import import MergeRegion L31–31
+ annotate_hunk_action import import annotate_hunk_action L31–31
+ annotations import import annotations L21–21
+ classify_action class import classify_action L31–31
+ compute_regions import import compute_regions L31–31
+ difflib import import difflib L23–23
+ format_conflict_diff import import format_conflict_diff L31–31
+ pathlib import import pathlib L24–24
+ pytest import import pytest L29–29
+ threading import import threading L25–25
+ three_way_merge_lines import import three_way_merge_lines L31–31
+ time import import time L26–26
~ muse/cli/commands/agent_config.py .py 1 symbol modified
~ muse/cli/commands/checkout.py .py 5 symbols added, 14 symbols modified
+ _checkout_with_merge function function _checkout_with_merge L273–457
+ diff_workdir_vs_snapshot import import diff_workdir_vs_snapshot L63–63
+ get_head_snapshot_manifest import import get_head_snapshot_manifest L64–64
+ read_object import import read_object L61–61
+ three_way_merge_lines import import three_way_merge_lines L81–81
~ run
~ muse/cli/commands/diff.py .py 5 symbols added, 2 symbols modified
+ _print_conflict_diff function function _print_conflict_diff L338–448
+ CONFLICT_SEPARATOR import import CONFLICT_SEPARATOR L66–66
+ annotate_hunk_action import import annotate_hunk_action L66–66
+ format_conflict_diff import import format_conflict_diff L66–66
+ read_merge_state import import read_merge_state L55–55
~ run
~ tests/test_cmd_agent_config.py .py 41 symbols added
+ TestDataIntegrity class class TestDataIntegrity L808–854
+ test_adapter_file_not_empty_after_sync method method test_adapter_file_not_empty_after_sync L822–830
+ test_corrupt_config_toml_falls_back_to_all_adapters method method test_corrupt_config_toml_falls_back_to_all_adapters L809–820
+ test_set_preserves_existing_config_integrity method method test_set_preserves_existing_config_integrity L841–854
+ test_sync_write_is_atomic method method test_sync_write_is_atomic L832–839
+ TestE2EFullWorkflow class class TestE2EFullWorkflow L681–743
+ test_init_set_sync_edit_status_resync method method test_init_set_sync_edit_status_resync L682–728
+ test_workspace_e2e method method test_workspace_e2e L730–743
+ TestLoadConfiguredAdapters class class TestLoadConfiguredAdapters L541–569
+ test_returns_list_when_set method method test_returns_list_when_set L553–557
+ test_returns_none_for_corrupt_toml method method test_returns_none_for_corrupt_toml L565–569
+ test_returns_none_for_malformed_list method method test_returns_none_for_malformed_list L559–563
+ test_returns_none_when_no_agent_config_section method method test_returns_none_when_no_agent_config_section L547–551
+ test_returns_none_when_no_config_toml method method test_returns_none_when_no_config_toml L542–545
+ TestPerformance class class TestPerformance L862–880
+ test_status_completes_under_1_second method method test_status_completes_under_1_second L872–880
+ test_sync_completes_under_2_seconds method method test_sync_completes_under_2_seconds L863–870
+ TestSecurity class class TestSecurity L888–934
+ test_agent_md_with_null_bytes_does_not_crash_sync method method test_agent_md_with_null_bytes_does_not_crash_sync L905–916
+ test_set_rejects_adapter_with_null_byte method method test_set_rejects_adapter_with_null_byte L897–903
+ test_set_rejects_path_traversal_in_adapter_name method method test_set_rejects_path_traversal_in_adapter_name L889–895
+ test_toml_injection_in_config_does_not_escape_section method method test_toml_injection_in_config_does_not_escape_section L918–934
+ TestSet class class TestSet L577–637
+ test_all_adapters_accepted method method test_all_adapters_accepted L632–637
+ test_json_schema method method test_json_schema L586–593
+ test_preserves_other_config_sections method method test_preserves_other_config_sections L607–614
+ test_single_adapter method method test_single_adapter L626–630
+ test_unknown_adapter_error_message method method test_unknown_adapter_error_message L621–624
+ test_unknown_adapter_exits_1 method method test_unknown_adapter_exits_1 L616–619
+ test_updates_existing_section method method test_updates_existing_section L595–605
+ test_writes_config_toml method method test_writes_config_toml L578–584
+ TestStress class class TestStress L751–800
+ test_concurrent_sync_no_corruption method method test_concurrent_sync_no_corruption L776–800
+ test_large_agent_md_syncs method method test_large_agent_md_syncs L752–763
+ test_rapid_sequential_syncs method method test_rapid_sequential_syncs L765–774
+ TestSyncPriorityChain class class TestSyncPriorityChain L645–673
+ test_cli_adapters_flag_overrides_config_toml method method test_cli_adapters_flag_overrides_config_toml L656–664
+ test_config_toml_limits_adapters method method test_config_toml_limits_adapters L646–654
+ test_no_config_generates_all_adapters method method test_no_config_generates_all_adapters L666–673
+ threading import import threading L74–74
+ time import import time L73–73
~ tests/test_cmd_checkout.py .py 29 symbols added, 2 symbols modified
+ TestCheckoutMergeClean class class TestCheckoutMergeClean L916–1013
+ test_clean_merge_json_output method method test_clean_merge_json_output L957–982
+ test_dry_run_does_not_switch_branch method method test_dry_run_does_not_switch_branch L998–1004
+ test_dry_run_json_reports_dry_run_true method method test_dry_run_json_reports_dry_run_true L1006–1013
+ test_no_dirty_files_succeeds_silently method method test_no_dirty_files_succeeds_silently L991–996
+ test_ours_only_change_carried_cleanly method method test_ours_only_change_carried_cleanly L927–955
+ test_switched_branch_recorded method method test_switched_branch_recorded L984–989
+ test_untracked_file_survives_checkout method method test_untracked_file_survives_checkout L919–925
+ TestCheckoutMergeConflict class class TestCheckoutMergeConflict L1016–1076
+ test_branch_is_switched_despite_conflict method method test_branch_is_switched_despite_conflict L1071–1076
+ test_conflict_json_output_contains_conflicts_list method method test_conflict_json_output_contains_conflicts_list L1062–1069
+ test_conflict_markers_contain_cohen_action_labels method method test_conflict_markers_contain_cohen_action_labels L1037–1044
+ test_conflict_markers_written_to_file method method test_conflict_markers_written_to_file L1027–1035
+ test_conflicting_change_exits_1 method method test_conflicting_change_exits_1 L1019–1025
+ test_merge_state_lists_conflict_path method method test_merge_state_lists_conflict_path L1054–1060
+ test_merge_state_written_on_conflict method method test_merge_state_written_on_conflict L1046–1052
+ TestCheckoutMergeErrors class class TestCheckoutMergeErrors L1079–1103
+ test_merge_already_on_branch_is_noop method method test_merge_already_on_branch_is_noop L1094–1098
+ test_merge_missing_branch_errors method method test_merge_missing_branch_errors L1089–1092
+ test_merge_with_create_flag_ignored method method test_merge_with_create_flag_ignored L1082–1087
+ test_merge_with_invalid_branch_name_errors method method test_merge_with_invalid_branch_name_errors L1100–1103
+ TestCheckoutMergeParser class class TestCheckoutMergeParser L880–913
+ _parse method method _parse L883–891
+ test_merge_and_dry_run_coexist method method test_merge_and_dry_run_coexist L905–908
+ test_merge_and_json_coexist method method test_merge_and_json_coexist L910–913
+ test_merge_false_by_default method method test_merge_false_by_default L901–903
+ test_merge_long_flag_parsed method method test_merge_long_flag_parsed L897–899
+ test_merge_short_flag_parsed method method test_merge_short_flag_parsed L893–895
+ _make_diverged_repo function function _make_diverged_repo L849–877
~ tests/test_cmd_diff.py .py 25 symbols added, 2 symbols modified
+ TestDiffConflictNoMerge class class TestDiffConflictNoMerge L974–987
+ test_no_merge_error_message_on_stderr method method test_no_merge_error_message_on_stderr L981–983
+ test_no_merge_in_progress_exits_1 method method test_no_merge_in_progress_exits_1 L977–979
+ test_no_merge_json_also_exits_1 method method test_no_merge_json_also_exits_1 L985–987
+ TestDiffConflictOutput class class TestDiffConflictOutput L990–1064
+ test_exits_nonzero_when_conflicts_exist method method test_exits_nonzero_when_conflicts_exist L993–996
+ test_json_conflict_entry_has_path_and_diffs method method test_json_conflict_entry_has_path_and_diffs L1036–1043
+ test_json_conflicts_list_non_empty method method test_json_conflicts_list_non_empty L1030–1034
+ test_json_labels_match_branches method method test_json_labels_match_branches L1045–1050
+ test_json_status_is_conflict method method test_json_status_is_conflict L1024–1028
+ test_output_contains_cohen_action_labels method method test_output_contains_cohen_action_labels L1013–1022
+ test_output_contains_ours_side method method test_output_contains_ours_side L1003–1006
+ test_output_contains_theirs_side method method test_output_contains_theirs_side L1008–1011
+ test_output_mentions_conflict_file method method test_output_mentions_conflict_file L998–1001
+ test_path_filter_limits_output method method test_path_filter_limits_output L1052–1057
+ test_path_filter_matching_includes_file method method test_path_filter_matching_includes_file L1059–1064
+ TestDiffConflictParser class class TestDiffConflictParser L942–971
+ _parse method method _parse L945–953
+ test_conflict_and_json_coexist method method test_conflict_and_json_coexist L963–966
+ test_conflict_and_path_coexist method method test_conflict_and_path_coexist L968–971
+ test_conflict_false_by_default method method test_conflict_false_by_default L959–961
+ test_conflict_flag_parsed method method test_conflict_flag_parsed L955–957
+ TestDiffConflictSecurity class class TestDiffConflictSecurity L1067–1079
+ test_ansi_in_conflict_path_sanitized method method test_ansi_in_conflict_path_sanitized L1070–1079
+ _make_conflict_repo function function _make_conflict_repo L909–939
~ tests/test_core_snapshot.py .py 22 symbols added, 5 symbols modified
+ TestNestedRepoWalk class class TestNestedRepoWalk L232–436
+ test_build_snapshot_manifest_excludes_nested method method test_build_snapshot_manifest_excludes_nested L310–318
+ test_concurrent_walks_consistent method method test_concurrent_walks_consistent L412–436
+ test_deeply_nested_repo_excluded method method test_deeply_nested_repo_excluded L269–279
+ test_diff_does_not_report_nested_files_as_added method method test_diff_does_not_report_nested_files_as_ L322–335
+ test_dirs_output_excludes_nested_repo method method test_dirs_output_excludes_nested_repo L297–306
+ test_large_parent_with_nested_repo_fast method method test_large_parent_with_nested_repo_fast L395–410
+ test_manifest_keys_posix_separators method method test_manifest_keys_posix_separators L339–347
+ test_multiple_nested_repos_all_excluded method method test_multiple_nested_repos_all_excluded L281–293
+ test_nested_muse_dir_itself_not_tracked method method test_nested_muse_dir_itself_not_tracked L349–354
+ test_nested_repo_files_excluded method method test_nested_repo_files_excluded L242–250
+ test_nested_repo_root_dir_excluded method method test_nested_repo_root_dir_excluded L252–257
+ test_nested_repo_with_unusual_name method method test_nested_repo_with_unusual_name L380–391
+ test_sibling_dirs_still_walked method method test_sibling_dirs_still_walked L259–267
+ test_symlink_to_nested_repo_not_followed method method test_symlink_to_nested_repo_not_followed L358–367
+ test_symlink_to_regular_dir_not_followed method method test_symlink_to_regular_dir_not_followed L369–378
+ _make_nested_repo function function _make_nested_repo L223–229
+ os import import os L13–13
+ threading import import threading L15–15
+ time import import time L16–16
+ walk_workdir import import walk_workdir L20–20
+ walk_workdir_with_dirs import import walk_workdir_with_dirs L20–20
← Older Oldest on task/cohen-transform
All commits
Newer → Latest on task/cohen-transform

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:2087dbb987c55a1ca7d5f8a956f9cef32356c609c98f18a48eb2f13b0c95e7ab --body "your comment"