gabriel / muse public
fix BREAKING diff task/diff-file-change-sigil #1 / 1
gabriel · 164 days ago · Apr 16, 2026 · Diff

fix(diff): use PatchOp.file_change for file-level sigil — not child op counts

The renderer inferred file status from child op direction: a PatchOp whose children were all DeleteOps was classified as 'D' (deleted), even when the file still existed in the working tree. This misled agents into treating a modified file that lost all its symbols as a deleted file.

Root cause: build_diff_ops knew which path bucket each file came from (added / removed / modified) but discarded that information, forcing the renderer to guess from children. _classify_patch_op and _op_category encoded this broken heuristic.

Fix: - Add file_change: NotRequired['added' | 'deleted' | 'modified'] to PatchOp - build_diff_ops sets file_change from the path bucket at construction time - _patch() forwards file_change to the PatchOp it wraps - _print_structured_delta reads op.get('file_change', 'modified') directly - JSON categorizer reads file_change instead of calling _op_category - Delete _classify_patch_op and _op_category (dead code) - Delete TestClassifyPatchOp and TestOpCategory (tested deleted functions) - Add 17 new tests covering the four cases (added/deleted/modified/renamed) at both unit and integration level

sha256:6aea7eaff3fd7a8d67ac602837a94e1724aed5e9140f201bc2770088d0c6d47d sha
+24 ~5 −21 symbols
sha256:6fbd1fcd94b73f132c7f017528f7b0bf6bf326dadf1d1b97d0c331ced57f1418 snapshot
+24
symbols added
~5
symbols modified
−21
symbols removed
0
dead code introduced
Semantic Changes 50 symbols
~ muse/cli/commands/diff.py .py 2 symbols removed, 2 symbols modified
− _classify_patch_op class function _classify_patch_op L121–140
− _op_category function function _op_category L143–149
~ run
~ muse/domain.py .py 1 symbol modified
~ tests/test_cmd_diff.py .py 24 symbols added, 19 symbols removed
− TestClassifyPatchOp class class TestClassifyPatchOp L151–201
− _make_delete_op method method _make_delete_op L160–166
− _make_insert_op method method _make_insert_op L152–158
− _make_patch method method _make_patch L168–177
− test_all_delete_returns_delete method method test_all_delete_returns_delete L185–189
− test_all_insert_returns_insert method method test_all_insert_returns_insert L179–183
− test_empty_child_ops_returns_replace method method test_empty_child_ops_returns_replace L197–201
− test_mixed_returns_replace method method test_mixed_returns_replace L191–195
− TestOpCategory class class TestOpCategory L209–283
− _make_delete method method _make_delete L218–224
− _make_insert method method _make_insert L210–216
− _make_move method method _make_move L235–241
− _make_replace method method _make_replace L226–233
− test_delete_op method method test_delete_op L248–251
− test_insert_op method method test_insert_op L243–246
− test_move_op_returns_replace method method test_move_op_returns_replace L258–261
− test_patch_with_all_deletes_returns_delete method method test_patch_with_all_deletes_returns_delete L274–283
− test_patch_with_all_inserts_returns_insert method method test_patch_with_all_inserts_returns_insert L263–272
− test_replace_op method method test_replace_op L253–256
+ TestBuildDiffOpsFileChange class class TestBuildDiffOpsFileChange L991–1085
+ _trees_with_symbols method method _trees_with_symbols L996–1010
+ test_added_file_patch_has_file_change_added method method test_added_file_patch_has_file_change_ L1012–1023
+ test_modified_file_all_symbol_additions_still_file_change_modified method method test_modified_file_all_symbol_additions_still_file_change_ L1070–1085
+ test_modified_file_all_symbol_deletions_still_file_change_modified method method test_modified_file_all_symbol_deletions_still_file_change_ L1051–1068
+ test_modified_file_patch_has_file_change_modified method method test_modified_file_patch_has_file_change_ L1038–1049
+ test_removed_file_patch_has_file_change_deleted method method test_removed_file_patch_has_file_change_deleted L1025–1036
+ TestFileSignilCorrectnessJsonOutput class class TestFileSignilCorrectnessJsonOutput L1196–1233
+ test_modified_file_gaining_all_symbols_in_modified_not_added method method test_modified_file_gaining_all_symbols_in_modified_not_ L1217–1233
+ test_modified_file_losing_all_symbols_in_modified_not_deleted method method test_modified_file_losing_all_symbols_in_modified_not_deleted L1199–1215
+ TestFileSignilCorrectnessTextOutput class class TestFileSignilCorrectnessTextOutput L1093–1193
+ test_actually_deleted_file_still_shows_D_sigil method method test_actually_deleted_file_still_shows_D_sigil L1155–1174
+ test_modified_file_gaining_all_symbols_shows_M_sigil method method test_modified_file_gaining_all_symbols_shows_M_sigil L1128–1153
+ test_modified_file_losing_all_symbols_shows_M_sigil method method test_modified_file_losing_all_symbols_shows_M_sigil L1102–1126
+ test_newly_added_file_still_shows_A_sigil method method test_newly_added_file_still_shows_A_sigil L1176–1193
+ TestInferenceHelpersRemoved class class TestInferenceHelpersRemoved L1241–1260
+ test_classify_patch_op_deleted class method test_classify_patch_op_deleted L1247–1253
+ test_op_category_deleted method method test_op_category_deleted L1255–1260
+ TestPatchOpFileChangeField class class TestPatchOpFileChangeField L947–983
+ _make_patch method method _make_patch L954–966
+ test_patch_op_accepts_file_change_added method method test_patch_op_accepts_file_change_ L968–970
+ test_patch_op_accepts_file_change_deleted method method test_patch_op_accepts_file_change_deleted L972–974
+ test_patch_op_accepts_file_change_modified method method test_patch_op_accepts_file_change_ L976–978
+ test_patch_op_file_change_is_optional method method test_patch_op_file_change_is_optional L980–983
← Older Oldest on task/diff-file-change-sigil
All commits
Newer → Latest on task/diff-file-change-sigil

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:6aea7eaff3fd7a8d67ac602837a94e1724aed5e9140f201bc2770088d0c6d47d --body "your comment"