gabriel / muse public
feat BREAKING harmony task/harmony-phase2 #1 / 1
AI Agent gabriel · 162 days ago · Apr 17, 2026 · Diff

feat(harmony): Phase 2 — CLI subcommand group + MIDI harmony rename

Replace the MIDI chord-analysis harmony.py with the Resolution Intelligence CLI, and rename the former to midi_harmony.py (kept for the midi rollout).

muse harmony subcommands (13 total): record — persist a ConflictPattern (idempotent) list — list patterns (--domain / --conflict-type filters) show — full pattern + all resolutions resolve — save a Resolution; CLI-level idempotency via content scan best — highest-quality resolution (human_verified > conf > applied) forget — delete pattern + resolutions clear — delete all patterns (--yes skips prompt) gc — GC stale unresolved patterns older than --age days policy-add — add/replace a declarative policy policy-list — scope-sorted policy listing policy-remove — remove a policy audit — append-only audit log (--limit)

app.py wiring: harmony.register(subparsers) added alongside mv midi_harmony commented out in the disabled midi rollout block

Test suite (tests/test_harmony_cli.py — 120 tests, all green): I Unit (21) — TypedDict schemas; subcommand help exits 0 II Integration (53) — all subcommand success paths (text + JSON) III Error (19) — invalid IDs, missing patterns, out-of-range confidence IV E2E (5) — record→resolve→best, policy lifecycle, gc, clear V Data integrity (6) — null fields always present, confidence round-trip VI Security (7) — path-traversal IDs rejected at every subcommand VII Performance (6) — all subcommands complete <300 ms

Combined: 255/255 green (135 Phase 1 + 120 Phase 2).

sha256:d203ff4c7b74475a36623ee4b8137bb36c0f3d26e4a89dfd64b404e64d3ec63c sha
+249 ~3 −13 symbols
sha256:7c215d619c33f3fd4a86b65b7261e41a3ce2e1133b4c7b200c40e7edc8e3f424 snapshot
+249
symbols added
~3
symbols modified
−13
symbols removed
0
dead code introduced
Semantic Changes 265 symbols
~ muse/cli/commands/midi_harmony.py .py 23 symbols added
+ _read_branch function function _read_branch L59–60 ← moved from muse/cli/commands/harmony.py
+ Counter import import Counter L40–40 ← moved from muse/cli/commands/harmony.py
+ ExitCode import import ExitCode L42–42
+ NoteInfo import import NoteInfo L45–45
+ _PITCH_CLASSES class import _PITCH_CLASSES L45–45
+ annotations import import annotations L33–33
+ argparse import import argparse L35–35
+ detect_chord import import detect_chord L45–45
+ json import import json L36–36
+ key_signature_guess import import key_signature_guess L45–45
+ load_track import import load_track L45–45
+ load_track_from_workdir import import load_track_from_workdir L45–45
+ logging import import logging L37–37
+ notes_by_bar import import notes_by_bar L45–45 ← moved from muse/cli/commands/harmony.py
+ pathlib import import pathlib L38–38
+ read_current_branch import import read_current_branch L44–44
+ read_repo_id import import read_repo_id L43–43
+ require_repo import import require_repo L43–43 ← moved from muse/cli/commands/harmony.py
+ resolve_commit_ref import import resolve_commit_ref L44–44 ← moved from muse/cli/commands/harmony.py
+ sys import import sys L39–39
+ logger variable variable logger L55–55
+ register function function register L63–69
+ run function function run L72–178 ← moved from muse/cli/commands/harmony.py
~ tests/test_harmony_cli.py .py 159 symbols added
+ TestAuditSuccess class class TestAuditSuccess L714–744
+ test_audit_empty method method test_audit_empty L717–722
+ test_audit_entry_has_required_fields method method test_audit_entry_has_required_fields L731–737
+ test_audit_limit method method test_audit_limit L739–744
+ test_audit_shows_entries_after_record method method test_audit_shows_entries_after_record L724–729
+ TestBestErrors class class TestBestErrors L863–868
+ test_best_invalid_id_exits_1 method method test_best_invalid_id_exits_1 L866–868
+ TestBestSuccess class class TestBestSuccess L488–513
+ test_best_returns_highest_quality method method test_best_returns_highest_quality L499–506
+ test_best_returns_null_when_no_resolution method method test_best_returns_null_when_no_resolution L491–497
+ test_best_text_output method method test_best_text_output L508–513
+ TestClearSuccess class class TestClearSuccess L540–559
+ test_clear_empty method method test_clear_empty L543–546
+ test_clear_removes_all method method test_clear_removes_all L548–553
+ test_clear_text_output method method test_clear_text_output L555–559
+ TestDataIntegrity class class TestDataIntegrity L994–1043
+ test_best_resolution_is_null_not_missing method method test_best_resolution_is_null_not_missing L1011–1016
+ test_gc_json_always_has_age_days method method test_gc_json_always_has_age_days L1018–1022
+ test_list_entry_fields_always_present method method test_list_entry_fields_always_present L997–1002
+ test_policy_list_null_conditions_present method method test_policy_list_null_conditions_present L1024–1036
+ test_resolve_confidence_round_trip method method test_resolve_confidence_round_trip L1038–1043
+ test_show_resolutions_field_present_when_empty method method test_show_resolutions_field_present_when_empty L1004–1009
+ TestEndToEnd class class TestEndToEnd L915–986
+ test_clear_empties_store method method test_clear_empties_store L964–969
+ test_forget_removes_from_list method method test_forget_removes_from_list L958–962
+ test_gc_does_not_remove_resolved_pattern method method test_gc_does_not_remove_resolved_pattern L971–986
+ test_policy_controls_match_then_remove method method test_policy_controls_match_then_remove L935–956
+ test_record_resolve_best_lifecycle method method test_record_resolve_best_lifecycle L918–933
+ TestForgetErrors class class TestForgetErrors L871–876
+ test_forget_invalid_id_exits_1 method method test_forget_invalid_id_exits_1 L874–876
+ TestForgetSuccess class class TestForgetSuccess L516–537
+ test_forget_existing_pattern method method test_forget_existing_pattern L519–525
+ test_forget_nonexistent_returns_false method method test_forget_nonexistent_returns_false L527–531
+ test_forget_text_output method method test_forget_text_output L533–537
+ TestGcErrors class class TestGcErrors L879–888
+ test_gc_invalid_age_exits_1 method method test_gc_invalid_age_exits_1 L882–884
+ test_gc_negative_age_exits_1 method method test_gc_negative_age_exits_1 L886–888
+ TestGcSuccess class class TestGcSuccess L562–587
+ test_gc_empty_store method method test_gc_empty_store L565–570
+ test_gc_removes_stale_unresolved method method test_gc_removes_stale_unresolved L572–583
+ test_gc_text_output method method test_gc_text_output L585–587
+ TestListSuccess class class TestListSuccess L317–370
+ test_list_empty_json method method test_list_empty_json L320–325
+ test_list_filter_by_conflict_type method method test_list_filter_by_conflict_type L364–370
+ test_list_filter_by_domain method method test_list_filter_by_domain L356–362
+ test_list_pattern_entry_has_required_fields method method test_list_pattern_entry_has_required_fields L335–341
+ test_list_resolution_count_increments method method test_list_resolution_count_increments L343–348
+ test_list_shows_recorded_pattern method method test_list_shows_recorded_pattern L327–333
+ test_list_text_output method method test_list_text_output L350–354
+ TestPerformance class class TestPerformance L1110–1171
+ test_gc_under_300ms method method test_gc_under_300ms L1167–1171
+ test_list_under_300ms method method test_list_under_300ms L1126–1131
+ test_policy_operations_under_300ms method method test_policy_operations_under_300ms L1153–1165
+ test_record_under_300ms method method test_record_under_300ms L1113–1124
+ test_resolve_under_300ms method method test_resolve_under_300ms L1140–1151
+ test_show_under_300ms method method test_show_under_300ms L1133–1138
+ TestPolicyAddSuccess class class TestPolicyAddSuccess L590–634
+ test_policy_add_json method method test_policy_add_json L593–606
+ test_policy_add_text_output method method test_policy_add_text_output L625–634
+ test_policy_add_with_condition method method test_policy_add_with_condition L608–623
+ TestPolicyErrors class class TestPolicyErrors L891–907
+ test_policy_add_invalid_id_exits_1 method method test_policy_add_invalid_id_exits_1 L894–903
+ test_policy_remove_invalid_id_exits_1 method method test_policy_remove_invalid_id_exits_1 L905–907
+ TestPolicyListSuccess class class TestPolicyListSuccess L637–687
+ test_policy_list_empty method method test_policy_list_empty L640–645
+ test_policy_list_entry_has_required_fields method method test_policy_list_entry_has_required_fields L660–672
+ test_policy_list_scope_sorted method method test_policy_list_scope_sorted L674–687
+ test_policy_list_shows_added method method test_policy_list_shows_ L647–658
+ TestPolicyRemoveSuccess class class TestPolicyRemoveSuccess L690–711
+ test_policy_remove_existing method method test_policy_remove_existing L693–705
+ test_policy_remove_nonexistent method method test_policy_remove_nonexistent L707–711
+ TestRecordErrors class class TestRecordErrors L752–798
+ test_record_bad_description_json_exits_1 method method test_record_bad_description_json_exits_1 L787–798
+ test_record_invalid_ours_id_exits_1 method method test_record_invalid_ours_id_exits_1 L775–785
+ test_record_missing_ours_id_exits_nonzero method method test_record_missing_ours_id_exits_nonzero L765–773
+ test_record_missing_path_exits_nonzero method method test_record_missing_path_exits_nonzero L755–763
+ TestRecordSuccess class class TestRecordSuccess L239–314
+ test_record_idempotent_sets_already_existed method method test_record_idempotent_sets_already_existed L257–274
+ test_record_json_returns_pattern_id method method test_record_json_returns_pattern_id L242–255
+ test_record_text_output method method test_record_text_output L276–286
+ test_record_with_description method method test_record_with_description L303–314
+ test_record_with_semantic_fingerprint method method test_record_with_semantic_fingerprint L288–301
+ TestRegistration class class TestRegistration L178–231
+ test_harmony_audit_help method method test_harmony_audit_help L229–231
+ test_harmony_best_help method method test_harmony_best_help L201–203
+ test_harmony_clear_help method method test_harmony_clear_help L209–211
+ test_harmony_forget_help method method test_harmony_forget_help L205–207
+ test_harmony_gc_help method method test_harmony_gc_help L213–215
+ test_harmony_help_exits_0 method method test_harmony_help_exits_0 L181–183
+ test_harmony_list_help method method test_harmony_list_help L189–191
+ test_harmony_policy_add_help method method test_harmony_policy_add_help L217–219
+ test_harmony_policy_list_help method method test_harmony_policy_list_help L221–223
+ test_harmony_policy_remove_help method method test_harmony_policy_remove_help L225–227
+ test_harmony_record_help method method test_harmony_record_help L185–187
+ test_harmony_resolve_help method method test_harmony_resolve_help L197–199
+ test_harmony_show_help method method test_harmony_show_help L193–195
+ TestResolveErrors class class TestResolveErrors L801–848
+ test_resolve_confidence_out_of_range_exits_1 method method test_resolve_confidence_out_of_range_exits_1 L826–836
+ test_resolve_invalid_pattern_id_exits_1 method method test_resolve_invalid_pattern_id_exits_1 L815–824
+ test_resolve_missing_pattern_exits_1 method method test_resolve_missing_pattern_exits_1 L804–813
+ test_resolve_negative_confidence_exits_1 method method test_resolve_negative_confidence_exits_1 L838–848
+ TestResolveSuccess class class TestResolveSuccess L408–485
+ test_resolve_human_verified method method test_resolve_human_verified L462–473
+ test_resolve_idempotent method method test_resolve_idempotent L428–444
+ test_resolve_json method method test_resolve_json L411–426
+ test_resolve_text_output method method test_resolve_text_output L475–485
+ test_resolve_with_agent_provenance method method test_resolve_with_agent_provenance L446–460
+ TestSecurity class class TestSecurity L1051–1102
+ test_best_path_traversal_rejected method method test_best_path_traversal_rejected L1074–1076
+ test_forget_path_traversal_rejected method method test_forget_path_traversal_rejected L1070–1072
+ test_policy_add_slash_in_id_rejected method method test_policy_add_slash_in_id_rejected L1078–1087
+ test_policy_remove_slash_in_id_rejected method method test_policy_remove_slash_in_id_rejected L1089–1091
+ test_record_path_traversal_ours_id_rejected method method test_record_path_traversal_ours_id_rejected L1054–1064
+ test_resolve_path_traversal_pattern_id_rejected method method test_resolve_path_traversal_pattern_id_rejected L1093–1102
+ test_show_path_traversal_rejected method method test_show_path_traversal_rejected L1066–1068
+ TestShowErrors class class TestShowErrors L851–860
+ test_show_invalid_id_exits_1 method method test_show_invalid_id_exits_1 L858–860
+ test_show_nonexistent_exits_1 method method test_show_nonexistent_exits_1 L854–856
+ TestShowSuccess class class TestShowSuccess L373–405
+ test_show_includes_resolutions method method test_show_includes_resolutions L384–390
+ test_show_pattern_json method method test_show_pattern_json L376–382
+ test_show_resolution_has_required_fields method method test_show_resolution_has_required_fields L392–399
+ test_show_text_output method method test_show_text_output L401–405
+ TestTypedDictSchemas class class TestTypedDictSchemas L106–175
+ _hints method method _hints L109–112
+ test_audit_json_has_entries method method test_audit_json_has_entries L174–175
+ test_audit_json_has_total method method test_audit_json_has_total L171–172
+ test_best_json_has_pattern_id method method test_best_json_has_pattern_id L141–142
+ test_best_json_has_resolution method method test_best_json_has_resolution L144–145
+ test_forget_json_has_pattern_id method method test_forget_json_has_pattern_id L147–148
+ test_forget_json_has_removed method method test_forget_json_has_ L150–151
+ test_list_json_has_patterns method method test_list_json_has_patterns L123–124
+ test_list_json_has_total method method test_list_json_has_total L120–121
+ test_policy_add_json_has_policy_id method method test_policy_add_json_has_policy_id L156–157
+ test_policy_list_json_has_policies method method test_policy_list_json_has_policies L162–163
+ test_policy_list_json_has_total method method test_policy_list_json_has_total L159–160
+ test_policy_remove_json_has_policy_id method method test_policy_remove_json_has_policy_id L165–166
+ test_policy_remove_json_has_removed method method test_policy_remove_json_has_ L168–169
+ test_record_json_has_already_existed method method test_record_json_has_already_existed L117–118
+ test_record_json_has_pattern_id method method test_record_json_has_pattern_id L114–115
+ test_resolve_json_has_already_existed method method test_resolve_json_has_already_existed L138–139
+ test_resolve_json_has_pattern_id method method test_resolve_json_has_pattern_id L135–136
+ test_resolve_json_has_resolution_id method method test_resolve_json_has_resolution_id L132–133
+ test_scalar_json_has_removed method method test_scalar_json_has_ L153–154
+ test_show_json_has_pattern method method test_show_json_has_pattern L126–127
+ test_show_json_has_resolutions method method test_show_json_has_resolutions L129–130
+ _fake_id function function _fake_id L32–33
+ _record function function _record L51–71
+ _resolve function function _resolve L74–98
+ CliRunner import import CliRunner L23–23
+ annotations import import annotations L13–13
+ hashlib import import hashlib L15–15
+ json import import json L16–16
+ pathlib import import pathlib L17–17
+ pytest import import pytest L21–21
+ time import import time L18–18
+ typing import import typing L19–19
+ repo function function repo L42–48
+ runner variable variable runner L25–25
~ muse/cli/app.py .py 1 symbol modified
~ main
~ muse/cli/commands/harmony.py .py 67 symbols added, 13 symbols removed, 2 symbols modified
− _read_branch function function _read_branch L59–60 → moved to muse/cli/commands/midi_harmony.py
− Counter import import Counter L40–40 → moved to muse/cli/commands/midi_harmony.py
− NoteInfo import import NoteInfo L45–45
− _PITCH_CLASSES class import _PITCH_CLASSES L45–45
− detect_chord import import detect_chord L45–45
− key_signature_guess import import key_signature_guess L45–45
− load_track import import load_track L45–45
− load_track_from_workdir import import load_track_from_workdir L45–45
− notes_by_bar import import notes_by_bar L45–45 → moved to muse/cli/commands/midi_harmony.py
− read_current_branch import import read_current_branch L44–44
− read_repo_id import import read_repo_id L43–43 → moved to muse/cli/commands/midi_harmony.py
− resolve_commit_ref import import resolve_commit_ref L44–44 → moved to muse/cli/commands/midi_harmony.py
− run function function run L72–178 → moved to muse/cli/commands/midi_harmony.py
+ _FORMAT_CHOICES variable variable _FORMAT_CHOICES L189–189
+ _HarmonyAuditJson class class _HarmonyAuditJson L331–335
+ _HarmonyBestJson class class _HarmonyBestJson L267–271
+ _HarmonyForgetJson class class _HarmonyForgetJson L274–278
+ _HarmonyGcJson class class _HarmonyGcJson L287–291
+ _HarmonyListEntryJson class class _HarmonyListEntryJson L204–213
+ _HarmonyListJson class class _HarmonyListJson L216–220
+ _HarmonyPatternDetailJson class class _HarmonyPatternDetailJson L223–236
+ _HarmonyPolicyAddJson class class _HarmonyPolicyAddJson L294–299
+ _HarmonyPolicyEntryJson class class _HarmonyPolicyEntryJson L302–314
+ _HarmonyPolicyListJson class class _HarmonyPolicyListJson L317–321
+ _HarmonyPolicyRemoveJson class class _HarmonyPolicyRemoveJson L324–328
+ _HarmonyRecordJson class class _HarmonyRecordJson L197–201
+ _HarmonyResolutionDetailJson class class _HarmonyResolutionDetailJson L239–249
+ _HarmonyResolveJson class class _HarmonyResolveJson L259–264
+ _HarmonyScalarJson class class _HarmonyScalarJson L281–284
+ _HarmonyShowJson class class _HarmonyShowJson L252–256
+ _check_format function function _check_format L407–413
+ _pattern_to_detail function function _pattern_to_detail L358–371
+ _pattern_to_list_entry function function _pattern_to_list_entry L343–355
+ _policy_to_entry function function _policy_to_entry L387–399
+ _resolution_to_detail function function _resolution_to_detail L374–384
+ AgentProvenance import import AgentProvenance L153–153
+ Any import import Any L150–150
+ AuditEvent import import AuditEvent L153–153
+ AuditEventType import import AuditEventType L153–153
+ ConflictPattern import import ConflictPattern L153–153
+ Policy import import Policy L153–153
+ PolicyCondition import import PolicyCondition L153–153
+ Resolution import import Resolution L153–153
+ ResolutionStrategy import import ResolutionStrategy L153–153
+ TypedDict import import TypedDict L150–150
+ _validate_id import import _validate_id L153–153
+ _validate_policy_id import import _validate_policy_id L153–153
+ append_audit import import append_audit L153–153
+ best_resolution import import best_resolution L153–153
+ blob_fingerprint import import blob_fingerprint L153–153
+ clamp_int import import clamp_int L185–185
+ clear_all import import clear_all L153–153
+ compute_pattern_id import import compute_pattern_id L153–153
+ compute_resolution_id import import compute_resolution_id L153–153
+ forget_pattern import import forget_pattern L153–153
+ gc_stale import import gc_stale L153–153
+ list_audit import import list_audit L153–153
+ list_patterns import import list_patterns L153–153
+ list_policies import import list_policies L153–153
+ list_resolutions import import list_resolutions L153–153
+ load_pattern import import load_pattern L153–153
+ load_policy import import load_policy L153–153
+ load_resolution import import load_resolution L153–153
+ record_pattern import import record_pattern L153–153
+ remove_policy import import remove_policy L153–153
+ sanitize_display import import sanitize_display L185–185
+ save_policy import import save_policy L153–153
+ save_resolution import import save_resolution L153–153
+ run_audit function function run_audit L1565–1595
+ run_best function function run_best L1210–1257
+ run_clear function function run_clear L1307–1350
+ run_forget function function run_forget L1265–1299
+ run_gc function function run_gc L1358–1395
+ run_list function function run_list L944–1003
+ run_policy_add function function run_policy_add L1403–1475
+ run_policy_list function function run_policy_list L1483–1515
+ run_policy_remove function function run_policy_remove L1523–1557
+ run_record function function run_record L828–936
+ run_resolve function function run_resolve L1079–1202
+ run_show function function run_show L1011–1071
← Older Oldest on task/harmony-phase2
All commits
Newer → Latest on task/harmony-phase2

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