gabriel / muse public
feat patch task/supercharge-patch-id #1 / 1
AI Agent gabriel · 163 days ago · Apr 17, 2026 · Diff

feat: supercharge patch-id with duration_ms, exit_code, files_changed, stable

- Add duration_ms (ms, 3dp), exit_code, files_changed, and stable to --json output. Agents now have the complete picture in one call: content identity, how many files changed, whether whitespace was normalised, and how long it took. - Update module docstring schema to reflect new fields. - Delete test_cmd_patch_id.py (14/15 tests were broken — raw hex object IDs used without required sha256: prefix). - Add test_patch_id_supercharge.py (54 tests, all GREEN): _compute_patch_id unit, JSON schema, text output, files_changed correctness, stable flag round-trip, cherry-pick detection, error paths, security (ANSI, null byte, path traversal, long ref), data integrity, performance, and stress (10 distinct commits).

sha256:7619040af537c856820b780fe64693daafcea745181a89b58eaa0e2b905241df sha
+91 ~1 −39 symbols
sha256:ad66d60c9ca8654894ae313665861f594681f2f1df87249e71434bfea43fc7dd snapshot
+91
symbols added
~1
symbol modified
−39
symbols removed
0
dead code introduced
Semantic Changes 131 symbols
~ tests/test_patch_id_supercharge.py .py 90 symbols added
+ TestComputePatchId class class TestComputePatchId L119–212
+ test_binary_content_included method method test_binary_content_included L202–212
+ test_deleted_file_affects_id method method test_deleted_file_affects_id L195–200
+ test_different_content_different_id method method test_different_content_different_id L137–144
+ test_file_order_does_not_affect_id method method test_file_order_does_not_affect_id L174–186
+ test_initial_commit_no_parent method method test_initial_commit_no_parent L188–193
+ test_no_op_commit_deterministic method method test_no_op_commit_deterministic L146–152
+ test_result_is_64_hex_chars method method test_result_is_64_hex_chars L129–135
+ test_same_diff_same_id method method test_same_diff_same_id L120–127
+ test_stable_normalizes_trailing_whitespace method method test_stable_normalizes_trailing_whitespace L154–162
+ test_unstable_sensitive_to_whitespace method method test_unstable_sensitive_to_whitespace L164–172
+ TestDataIntegrity class class TestDataIntegrity L555–590
+ test_adding_file_changes_patch_id method method test_adding_file_changes_patch_id L572–580
+ test_patch_id_changes_when_content_changes method method test_patch_id_changes_when_content_changes L556–570
+ test_patch_id_stable_vs_unstable_differ_for_ws method method test_patch_id_stable_vs_unstable_differ_for_ws L582–590
+ TestErrors class class TestErrors L485–510
+ test_bad_ref_exits_nonzero method method test_bad_ref_exits_nonzero L491–496
+ test_empty_repo_exits_nonzero method method test_empty_repo_exits_nonzero L486–489
+ test_error_to_stderr_not_stdout method method test_error_to_stderr_not_stdout L506–510
+ test_no_traceback_on_bad_ref method method test_no_traceback_on_bad_ref L498–504
+ TestFilesChanged class class TestFilesChanged L435–478
+ test_deletion_counted method method test_deletion_counted L445–451
+ test_initial_commit_all_files_counted method method test_initial_commit_all_files_counted L436–443
+ test_modification_counted method method test_modification_counted L453–460
+ test_no_op_commit_zero_files_changed method method test_no_op_commit_zero_files_changed L472–478
+ test_unchanged_files_not_counted method method test_unchanged_files_not_counted L462–470
+ TestJsonGreen class class TestJsonGreen L311–396
+ test_branch_name_ref method method test_branch_name_ref L391–396
+ test_commit_id_matches_head method method test_commit_id_matches_head L341–346
+ test_commit_id_present method method test_commit_id_present L312–317
+ test_different_diff_different_patch_id method method test_different_diff_different_patch_id L367–382
+ test_explicit_commit_id_ref method method test_explicit_commit_id_ref L384–389
+ test_patch_id_is_64_hex method method test_patch_id_is_64_hex L326–332
+ test_patch_id_present method method test_patch_id_present L319–324
+ test_same_diff_same_patch_id method method test_same_diff_same_patch_id L348–365
+ test_subject_present method method test_subject_present L334–339
+ TestJsonSupercharge class class TestJsonSupercharge L219–304
+ test_json_duration_ms_non_negative method method test_json_duration_ms_non_negative L229–234
+ test_json_exit_code_zero_on_success method method test_json_exit_code_zero_on_success L243–248
+ test_json_files_changed_correct_count method method test_json_files_changed_correct_count L257–263
+ test_json_files_changed_is_int method method test_json_files_changed_is_int L265–270
+ test_json_has_duration_ms method method test_json_has_duration_ms L222–227
+ test_json_has_exit_code method method test_json_has_exit_code L236–241
+ test_json_has_files_changed method method test_json_has_files_changed L250–255
+ test_json_has_stable_field method method test_json_has_stable_field L272–277
+ test_json_stable_false_by_default method method test_json_stable_false_by_default L279–284
+ test_json_stable_true_with_flag method method test_json_stable_true_with_flag L286–291
+ test_stable_flag_produces_different_patch_id_for_ws_diff method method test_stable_flag_produces_different_patch_id_for_ws_diff L293–304
+ TestPerformance class class TestPerformance L597–614
+ test_duration_ms_non_negative method method test_duration_ms_non_negative L609–614
+ test_duration_ms_under_two_seconds method method test_duration_ms_under_two_seconds L598–607
+ TestSecurity class class TestSecurity L517–548
+ test_ansi_in_ref_rejected method method test_ansi_in_ref_rejected L518–522
+ test_no_traceback_on_ansi_ref method method test_no_traceback_on_ansi_ref L542–548
+ test_null_byte_in_ref_rejected method method test_null_byte_in_ref_rejected L524–528
+ test_path_traversal_in_ref_rejected method method test_path_traversal_in_ref_rejected L530–534
+ test_very_long_ref_rejected method method test_very_long_ref_rejected L536–540
+ TestStress class class TestStress L621–645
+ test_10_distinct_commits_10_distinct_patch_ids method method test_10_distinct_commits_10_distinct_patch_ids L622–633
+ test_50_file_commit method method test_50_file_commit L635–645
+ TestTextOutput class class TestTextOutput L403–428
+ test_text_commit_id_matches_json method method test_text_commit_id_matches_json L421–428
+ test_text_format_two_parts method method test_text_format_two_parts L404–411
+ test_text_patch_id_is_hex method method test_text_patch_id_is_hex L413–419
+ _REPO_ID variable variable _REPO_ID L51–51 ← moved from tests/test_cmd_patch_id.py
+ _TS variable variable _TS L50–50
+ _commit function function _commit L80–100
+ _init_repo function function _init_repo L63–71
+ _json_out function function _json_out L107–112
+ _oid function function _oid L58–60
+ _pid function function _pid L103–104
+ _write_obj function function _write_obj L74–77
+ CliRunner import import CliRunner L46–46
+ CommitRecord import import CommitRecord L44–44
+ InvokeResult import import InvokeResult L46–46
+ Manifest import import Manifest L45–45 ← moved from tests/test_cmd_patch_id.py
+ SnapshotRecord import import SnapshotRecord L44–44
+ _compute_patch_id import import _compute_patch_id L41–41
+ annotations import import annotations L32–32 ← moved from tests/test_cmd_patch_id.py
+ compute_commit_id import import compute_commit_id L43–43
+ compute_snapshot_id import import compute_snapshot_id L43–43 ← moved from tests/test_cmd_patch_id.py
+ datetime import import datetime L34–34 ← moved from tests/test_cmd_patch_id.py
+ hashlib import import hashlib L35–35 ← moved from tests/test_cmd_patch_id.py
+ json import import json L36–36 ← moved from tests/test_cmd_patch_id.py
+ pathlib import import pathlib L37–37 ← moved from tests/test_cmd_patch_id.py
+ pytest import import pytest L39–39 ← moved from tests/test_cmd_patch_id.py
+ write_commit import import write_commit L44–44
+ write_object import import write_object L42–42 ← moved from tests/test_cmd_patch_id.py
+ write_snapshot import import write_snapshot L44–44 ← moved from tests/test_cmd_patch_id.py
+ runner variable variable runner L48–48 ← moved from tests/test_cmd_patch_id.py
~ tests/test_cmd_patch_id.py .py 39 symbols removed
− _REPO_ID variable variable _REPO_ID L31–31 → moved to tests/test_patch_id_supercharge.py
− _commit_files function function _commit_files L59–96
− _counter variable variable _counter L32–32
− _env function function _env L55–56
− _init_repo function function _init_repo L44–52
− _invoke function function _invoke L99–101
− _sha function function _sha L40–41
− CliRunner import import CliRunner L23–23
− CommitRecord import import CommitRecord L26–26
− Manifest import import Manifest L27–27 → moved to tests/test_patch_id_supercharge.py
− SnapshotRecord import import SnapshotRecord L26–26
− annotations import import annotations L14–14 → moved to tests/test_patch_id_supercharge.py
− compute_commit_id import import compute_commit_id L25–25
− compute_snapshot_id import import compute_snapshot_id L25–25 → moved to tests/test_patch_id_supercharge.py
− datetime import import datetime L16–16 → moved to tests/test_patch_id_supercharge.py
− hashlib import import hashlib L17–17 → moved to tests/test_patch_id_supercharge.py
− json import import json L18–18 → moved to tests/test_patch_id_supercharge.py
− pathlib import import pathlib L19–19 → moved to tests/test_patch_id_supercharge.py
− pytest import import pytest L21–21 → moved to tests/test_patch_id_supercharge.py
− write_commit import import write_commit L26–26
− write_object import import write_object L24–24 → moved to tests/test_patch_id_supercharge.py
− write_snapshot import import write_snapshot L26–26 → moved to tests/test_patch_id_supercharge.py
− runner variable variable runner L29–29 → moved to tests/test_patch_id_supercharge.py
− test_compute_patch_id_different_diff_different_id function function test_compute_patch_id_different_diff_different_id L120–131
− test_compute_patch_id_empty_diff_is_deterministic function function test_compute_patch_id_empty_diff_is_deterministic L134–142
− test_compute_patch_id_same_diff_same_id function function test_compute_patch_id_same_diff_same_id L109–117
− test_compute_patch_id_stable_normalizes_whitespace function function test_compute_patch_id_stable_normalizes_whitespace L145–160
− test_compute_patch_id_without_stable_is_sensitive_to_whitespace function function test_compute_patch_id_without_stable_is_sensitive_to_whitespace L163–177
− test_patch_id_ansi_in_ref_rejected function function test_patch_id_ansi_in_ref_rejected L303–307
− test_patch_id_branch_name_ref function function test_patch_id_branch_name_ref L271–277
− test_patch_id_different_commits_have_different_ids function function test_patch_id_different_commits_have_different_ids L315–330
− test_patch_id_empty_repo_exits_nonzero function function test_patch_id_empty_repo_exits_nonzero L292–295
− test_patch_id_json_commit_id_matches_head function function test_patch_id_json_commit_id_matches_head L207–214
− test_patch_id_json_schema_keys function function test_patch_id_json_schema_keys L185–195
− test_patch_id_json_subject_is_commit_message function function test_patch_id_json_subject_is_commit_message L198–204
− test_patch_id_nonexistent_ref_exits_nonzero function function test_patch_id_nonexistent_ref_exits_nonzero L285–289
− test_patch_id_same_diff_same_id_across_commits function function test_patch_id_same_diff_same_id_across_commits L241–253
− test_patch_id_specific_commit_id function function test_patch_id_specific_commit_id L261–268
− test_patch_id_text_output_has_two_parts function function test_patch_id_text_output_has_two_parts L222–233
~ muse/cli/commands/patch_id.py .py 1 symbol added, 1 symbol modified
+ time import import time L63–63
~ run
← Older Oldest on task/supercharge-patch-id
All commits
Newer → Latest on task/supercharge-patch-id

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:7619040af537c856820b780fe64693daafcea745181a89b58eaa0e2b905241df --body "your comment"