gabriel / musehub public
feat BREAKING main #20 / 34
AI Agent gabriel · 126 days ago · May 9, 2026 · Diff

feat: TDD proposal symbol delta — server-side change map with delta algebra

New service: proposal_symbol_delta.build_proposal_symbol_delta - Reads structured_delta from each commit in chronological order - Maps delta algebra ops: insert→added, delete→deleted, replace/mutate/patch→modified - Reduces net op across commits (insert+delete=cancelled, delete+insert=modified, etc.) - Groups entries by file path, sorted added→modified→deleted within each file - Surfaces breaking change flags from commit.breaking_changes

Controller (ui_proposals.py): wires the service, passes ProposalSymbolDelta to template.

Template (proposal_detail.html): replaces empty JS-dependent columns with SSR delta: - Ratio bar: proportional add/modify/delete segments as visual fingerprint - File-grouped entries with op sigils (+/~/×) and inline breaking markers - Symbol names link to /symbols?q=<name> - Empty state when no structured_delta present

Tests: 26 unit tests (net-op reduction, op mapping, grouping, deduplication, breaking flags) + 13 SSR integration tests (HTML rendering assertions).

sha256:70b3c5549b31d5595edc8f835f4cc5c7acf75b94ab9ce7559bac1d4b9e9196b5 sha
+80 ~7 symbols
sha256:37489fe4cf01439c423e9b25be9b6adedc3dc9c834bc8e311e84c91f37d5d870 snapshot
+80
symbols added
~7
symbols modified
0
dead code introduced
Semantic Changes 87 symbols
+ ProposalSymbolDelta class class ProposalSymbolDelta L43–51
+ total method method total L50–51
+ SymbolDeltaEntry class class SymbolDeltaEntry L34–39
+ _DELETE_OPS variable variable _DELETE_OPS L59–59
+ _INSERT_OPS variable variable _INSERT_OPS L58–58
+ _REDUCTION variable variable _REDUCTION L75–88
+ _classify class function _classify L62–67
+ _reduce function function _reduce L91–92
+ build_proposal_symbol_delta function function build_proposal_symbol_delta L100–181
+ annotations import import annotations L23–23
+ dataclass class import dataclass L25–25
+ field import import field L25–25
+ _delta_with function function _delta_with L112–123
+ _make_commit function async_function _make_commit L86–109
+ _make_proposal function async_function _make_proposal L60–83
+ _make_repo function async_function _make_repo L40–57
+ AsyncClient import import AsyncClient L27–27
+ AsyncSession import import AsyncSession L28–28
+ MusehubCommit import import MusehubCommit L32–32
+ MusehubProposal import import MusehubProposal L32–32
+ MusehubRepo import import MusehubRepo L32–32
+ annotations import import annotations L22–22
+ compute_identity_id import import compute_identity_id L31–31
+ compute_proposal_id import import compute_proposal_id L31–31
+ compute_repo_id import import compute_repo_id L31–31
+ datetime import import datetime L24–24
+ fake_id import import fake_id L30–30
+ now_utc_iso import import now_utc_iso L30–30
+ pytest import import pytest L26–26
+ timezone import import timezone L24–24
+ test_added_symbol_name_rendered_server_side function async_function test_added_symbol_name_rendered_server_side L147–162
+ test_added_then_deleted_symbol_not_rendered function async_function test_added_then_deleted_symbol_not_rendered L358–404
+ test_breaking_change_marker_rendered_inline function async_function test_breaking_change_marker_rendered_inline L262–281
+ test_deleted_symbol_name_rendered_server_side function async_function test_deleted_symbol_name_rendered_server_side L183–198
+ test_delta_section_present_in_html function async_function test_delta_section_present_in_html L131–144
+ test_empty_delta_renders_empty_state function async_function test_empty_delta_renders_empty_state L284–296
+ test_file_path_rendered_as_group_header function async_function test_file_path_rendered_as_group_header L201–216
+ test_modified_symbol_name_rendered_server_side function async_function test_modified_symbol_name_rendered_server_side L165–180
+ test_multiple_files_each_rendered_as_group function async_function test_multiple_files_each_rendered_as_group L335–355
+ test_op_sigil_present_for_each_entry function async_function test_op_sigil_present_for_each_entry L299–314
+ test_ratio_bar_present_when_symbols_exist function async_function test_ratio_bar_present_when_symbols_exist L243–259
+ test_sym_counts_rendered_server_side function async_function test_sym_counts_rendered_server_side L219–240
+ test_symbol_link_points_to_symbols_page function async_function test_symbol_link_points_to_symbols_page L317–332
~ tests/test_proposal_symbol_delta.py .py 36 symbols added
+ _commit function function _commit L46–73
+ _commit_no_delta function function _commit_no_delta L76–83
+ Any import import Any L31–31
+ ProposalSymbolDelta import import ProposalSymbolDelta L35–35
+ SymbolDeltaEntry import import SymbolDeltaEntry L35–35
+ annotations import import annotations L28–28
+ build_proposal_symbol_delta import import build_proposal_symbol_delta L35–35
+ datetime import import datetime L30–30
+ pytest import import pytest L33–33
+ timezone import import timezone L30–30
+ test_address_parsed_into_file_and_symbol_name function function test_address_parsed_into_file_and_symbol_name L185–192
+ test_address_with_multiple_colons_handled function function test_address_with_multiple_colons_handled L195–201
+ test_breaking_changes_collected_from_commits function function test_breaking_changes_collected_from_commits L372–376
+ test_breaking_changes_flagged_on_matching_entry function function test_breaking_changes_flagged_on_matching_entry L356–361
+ test_breaking_flag_propagates_through_net_op_reduction function function test_breaking_flag_propagates_through_net_op_reduction L379–386
+ test_by_file_sorts_added_before_modified_before_deleted function function test_by_file_sorts_added_before_modified_before_deleted L229–241
+ test_commit_with_no_structured_delta_skipped function function test_commit_with_no_structured_delta_skipped L100–102
+ test_deduplication_last_commit_wins function function test_deduplication_last_commit_wins L264–273
+ test_delete_op_maps_to_deleted function function test_delete_op_maps_to_deleted L120–127
+ test_empty_commits_returns_empty_delta function function test_empty_commits_returns_empty_delta L91–97
+ test_entries_are_symbol_delta_entry_instances function function test_entries_are_symbol_delta_entry_instances L399–403
+ test_file_level_op_without_double_colon_excluded function function test_file_level_op_without_double_colon_excluded L165–182
+ test_insert_op_maps_to_added function function test_insert_op_maps_to_ L110–117
+ test_multiple_commits_multiple_files function function test_multiple_commits_multiple_files L334–348
+ test_mutate_op_maps_to_modified function function test_mutate_op_maps_to_ L138–143
+ test_net_op_add_then_delete_cancels_to_nothing function function test_net_op_add_then_delete_cancels_to_nothing L293–301
+ test_net_op_add_then_modify_stays_added function function test_net_op_add_then_modify_stays_ L281–290
+ test_net_op_deleted_then_insert_is_modified function function test_net_op_deleted_then_insert_is_ L316–326
+ test_net_op_modify_then_delete_is_deleted function function test_net_op_modify_then_delete_is_deleted L304–313
+ test_non_breaking_entry_not_flagged function function test_non_breaking_entry_not_flagged L364–369
+ test_patch_op_maps_to_modified function function test_patch_op_maps_to_ L146–150
+ test_replace_op_maps_to_modified function function test_replace_op_maps_to_ L130–135
+ test_returns_proposal_symbol_delta_instance function function test_returns_proposal_symbol_delta_instance L394–396
+ test_symbols_grouped_by_file function function test_symbols_grouped_by_file L209–226
+ test_total_count_is_sum_of_all_buckets function function test_total_count_is_sum_of_all_buckets L244–256
+ test_unknown_op_treated_as_modified function function test_unknown_op_treated_as_ L153–157
~ musehub/api/routes/musehub/ui_proposals.py .py 1 symbol added, 1 symbol modified
+ build_proposal_symbol_delta import import build_proposal_symbol_delta L39–39

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