gabriel / musehub public
feat BREAKING dev
AI Agent gabriel · 129 days ago · May 7, 2026 · Diff

feat: enriched repo cards with live intel signals on domain detail page

- Add enrich_repo_cards() service — 5-query batch enrichment (pulse, autonomy, hottest symbol, blast leader, dead/breakage health) with zero N+1 queries - Add repo_card_enriched.html partial with spectral sparkline SVG, autonomy progress bar, health donut gauge (reusing intel-health-fill tiers), hottest and blast symbol rows, and dead/error/warn counts in fixed 3-column grid - Add _repo_cards.scss component with all rc-* styles; swap dd-repo-grid for rc-grid - Add skull icon to sprite; use on intel dashboard, intel dead page, and repo cards - Use domain-code blue (#60a5fa) for sparkline bars with opacity encoding commit intensity, matching the profile activity heatmap approach - Wire enrich_repo_cards into ui_domains.py domain detail route - Replace gradient branding with solid accent blue on /muse, /domains headers - Remove new-repo-from-domain route (MSign auth not yet in GUI) - Rename artifact_types → kinds; fix install_count; add directory dimension - Add 74 tests across 8 tiers: unit, integration, e2e, stress, state, integrity, performance, security (T100–T805)

sha256:1066bd2893ed5a5ace0322bbb97ecc9babf891a25751fadbb76efc65d41d2e66 sha
+246 ~4 −2 symbols
sha256:109b365dedd3221484e6e1d04fb8146e3bd691362ee5f34ef46443c7da296d29 snapshot
+246
symbols added
~4
symbols modified
−2
symbols removed
0
dead code introduced
Semantic Changes 252 symbols
+ PulseBucket class class PulseBucket L110–127
+ RepoCardEnrichment class class RepoCardEnrichment L153–203
+ health_status method method health_status L189–203
+ SymbolStat class class SymbolStat L131–149
+ name method method name L147–149
+ _PULSE_COLOR variable variable _PULSE_COLOR L210–210
+ _PULSE_DAYS variable variable _PULSE_DAYS L106–106
+ _SPARKLINE_HEIGHT variable variable _SPARKLINE_HEIGHT L105–105
+ _SPECTRAL_STOPS variable variable _SPECTRAL_STOPS L52–57
+ _build_pulse function function _build_pulse L255–271
+ _lerp function function _lerp L60–62
+ _make_empty_buckets function function _make_empty_buckets L213–225
+ _normalise_heights function function _normalise_heights L228–252
+ _short_name function function _short_name L274–276
+ enrich_repo_cards function async_function enrich_repo_cards L283–416
+ AsyncSession import import AsyncSession L46–46
+ annotations import import annotations L39–39
+ colorsys import import colorsys L41–41
+ dataclass class import dataclass L42–42
+ date import import date L43–43
+ datetime import import datetime L43–43
+ field import import field L42–42
+ sa import import sa L45–45
+ timedelta import import timedelta L43–43
+ timezone import import timezone L43–43
+ spectral_color function function spectral_color L65–98
~ tests/test_repo_card_e2e.py .py 35 symbols added
+ _add_agent_commit function async_function _add_agent_commit L112–119
+ _attach_repo_to_domain function async_function _attach_repo_to_domain L80–90
+ _domain_id function function _domain_id L48–49
+ _domain_url function function _domain_url L149–150
+ _insert_dead function async_function _insert_dead L136–146
+ _insert_symbol_intel function async_function _insert_symbol_intel L122–133
+ _make_domain function async_function _make_domain L52–77
+ _make_public_repo function async_function _make_public_repo L93–109
+ _utc_now function function _utc_now L44–45
+ AsyncClient import import AsyncClient L26–26
+ AsyncSession import import AsyncSession L27–27
+ MusehubDomain import import MusehubDomain L30–30
+ MusehubIntelDead import import MusehubIntelDead L31–31
+ MusehubRepo import import MusehubRepo L31–31
+ MusehubSymbolIntel import import MusehubSymbolIntel L31–31
+ annotations import import annotations L20–20
+ compute_identity_id import import compute_identity_id L36–36
+ compute_repo_id import import compute_repo_id L36–36
+ create_commit import import create_commit L37–37
+ create_repo import import create_repo L37–37
+ datetime import import datetime L23–23
+ pytest import import pytest L25–25
+ secrets import import secrets L22–22
+ text import import text L28–28
+ timedelta import import timedelta L23–23
+ timezone import import timezone L23–23
+ test_t300_domain_detail_returns_rc_cards function async_function test_t300_domain_detail_returns_rc_cards L158–170
+ test_t301_sparkline_rendered_for_repo_with_commits function async_function test_t301_sparkline_rendered_for_repo_with_commits L178–190
+ test_t302_health_badge_risk_when_errors function async_function test_t302_health_badge_risk_when_errors L198–220
+ test_t302b_health_badge_warn_when_dead function async_function test_t302b_health_badge_warn_when_dead L224–236
+ test_t303_autonomy_pct_rendered_for_all_agent_repo function async_function test_t303_autonomy_pct_rendered_for_all_agent_repo L244–259
+ test_t304_hottest_symbol_rendered function async_function test_t304_hottest_symbol_rendered L267–282
+ test_t305_blast_leader_rendered function async_function test_t305_blast_leader_rendered L290–305
+ test_t306_clean_card_when_no_intel function async_function test_t306_clean_card_when_no_intel L313–326
+ test_t307_json_format_not_broken_by_enrichment function async_function test_t307_json_format_not_broken_by_enrichment L334–351
+ _add_agent_commit function async_function _add_agent_commit L121–136
+ _add_human_commit function async_function _add_human_commit L139–145
+ _commit_id function function _commit_id L59–60
+ _days_ago function function _days_ago L55–56
+ _insert_breakage_meta function async_function _insert_breakage_meta L101–118
+ _insert_dead function async_function _insert_dead L82–98
+ _insert_symbol_intel function async_function _insert_symbol_intel L63–79
+ _utc_now function function _utc_now L51–52
+ AsyncSession import import AsyncSession L33–33
+ MusehubCommit import import MusehubCommit L35–35
+ MusehubIntelBreakageMeta import import MusehubIntelBreakageMeta L35–35
+ MusehubIntelDead import import MusehubIntelDead L35–35
+ MusehubSymbolIntel import import MusehubSymbolIntel L35–35
+ _PULSE_DAYS import import _PULSE_DAYS L41–41
+ annotations import import annotations L26–26
+ create_commit import import create_commit L45–45
+ create_repo import import create_repo L45–45
+ datetime import import datetime L29–29
+ enrich_repo_cards import import enrich_repo_cards L41–41
+ pytest import import pytest L31–31
+ pytest_asyncio import import pytest_asyncio L32–32
+ secrets import import secrets L28–28
+ timedelta import import timedelta L29–29
+ timezone import import timezone L29–29
+ test_t200_pulse_correct_daily_counts function async_function test_t200_pulse_correct_daily_counts L153–174
+ test_t201_autonomy_pct_100_all_agent function async_function test_t201_autonomy_pct_100_all_agent L182–189
+ test_t202_autonomy_pct_0_all_human function async_function test_t202_autonomy_pct_0_all_human L193–200
+ test_t203_autonomy_pct_mixed function async_function test_t203_autonomy_pct_mixed L204–212
+ test_t204_hottest_symbol_highest_churn function async_function test_t204_hottest_symbol_highest_churn L220–232
+ test_t205_blast_leader_highest_blast function async_function test_t205_blast_leader_highest_blast L236–247
+ test_t206_dead_count_only_high_confidence function async_function test_t206_dead_count_only_high_confidence L255–263
+ test_t207_health_risk_when_breakage_errors function async_function test_t207_health_risk_when_breakage_errors L271–277
+ test_t208_health_warn_when_dead_no_errors function async_function test_t208_health_warn_when_dead_no_errors L281–287
+ test_t209_health_clean_no_data function async_function test_t209_health_clean_no_data L291–296
+ test_t210_batches_multiple_repos function async_function test_t210_batches_multiple_repos L304–318
+ test_t211_safe_zero_value_no_intel function async_function test_t211_safe_zero_value_no_intel L326–339
+ test_t212_pulse_always_30_buckets function async_function test_t212_pulse_always_30_buckets L347–355
+ test_t213_commits_older_than_30d_excluded_from_pulse function async_function test_t213_commits_older_than_30d_excluded_from_pulse L363–372
+ test_t214_hottest_symbol_none_when_no_symbol_intel function async_function test_t214_hottest_symbol_none_when_no_symbol_intel L380–385
+ test_t215_blast_leader_none_when_all_blast_zero function async_function test_t215_blast_leader_none_when_all_blast_zero L389–395
+ _HEX_RE variable variable _HEX_RE L181–181
+ AsyncMock import import AsyncMock L32–32
+ MagicMock import import MagicMock L32–32
+ PulseBucket import import PulseBucket L36–36
+ RepoCardEnrichment import import RepoCardEnrichment L36–36
+ SymbolStat import import SymbolStat L36–36
+ _PULSE_DAYS import import _PULSE_DAYS L36–36
+ _SPARKLINE_HEIGHT import import _SPARKLINE_HEIGHT L36–36
+ _build_pulse import import _build_pulse L36–36
+ _make_empty_buckets import import _make_empty_buckets L36–36
+ _normalise_heights import import _normalise_heights L36–36
+ annotations import import annotations L28–28
+ date import import date L31–31
+ enrich_repo_cards import import enrich_repo_cards L36–36
+ pytest import import pytest L34–34
+ re import import re L30–30
+ spectral_color import import spectral_color L36–36
+ timedelta import import timedelta L31–31
+ test_t100_health_clean_when_all_zero function function test_t100_health_clean_when_all_zero L53–56
+ test_t101_health_warn_when_dead_nonzero function function test_t101_health_warn_when_dead_nonzero L59–62
+ test_t101b_health_warn_when_warnings_nonzero function function test_t101b_health_warn_when_warnings_nonzero L65–68
+ test_t102_health_risk_when_errors_nonzero function function test_t102_health_risk_when_errors_nonzero L71–74
+ test_t102b_health_risk_dominates_warn function function test_t102b_health_risk_dominates_warn L77–80
+ test_t103_symbol_stat_name_extracts_after_double_colon function function test_t103_symbol_stat_name_extracts_after_double_colon L87–90
+ test_t103b_symbol_stat_name_handles_nested_path function function test_t103b_symbol_stat_name_handles_nested_path L93–96
+ test_t104_symbol_stat_name_returns_full_address_when_no_separator function function test_t104_symbol_stat_name_returns_full_address_when_no_separator L99–102
+ test_t105_default_autonomy_pct_is_zero function function test_t105_default_autonomy_pct_is_zero L109–112
+ test_t106_build_pulse_always_30_buckets function function test_t106_build_pulse_always_30_buckets L119–123
+ test_t107_build_pulse_zero_fills_missing_days function function test_t107_build_pulse_zero_fills_missing_days L126–133
+ test_t108_build_pulse_h_zero_for_zero_count function function test_t108_build_pulse_h_zero_for_zero_count L136–142
+ test_t109_normalise_sets_max_to_sparkline_height function function test_t109_normalise_sets_max_to_sparkline_height L149–156
+ test_t110_normalise_minimum_height_two_for_nonzero function function test_t110_normalise_minimum_height_two_for_nonzero L159–166
+ test_t110b_normalise_noop_when_all_zero function function test_t110b_normalise_noop_when_all_zero L169–174
+ test_t111_spectral_color_first_is_blue function function test_t111_spectral_color_first_is_blue L184–186
+ test_t112_spectral_color_last_is_pink function function test_t112_spectral_color_last_is_pink L189–191
+ test_t113_spectral_color_valid_hex_for_all_positions function function test_t113_spectral_color_valid_hex_for_all_positions L194–198
+ test_t114_spectral_color_total_one_no_division_error function function test_t114_spectral_color_total_one_no_division_error L201–204
+ test_t115_spectral_gradient_traverses_full_range function function test_t115_spectral_gradient_traverses_full_range L207–211
+ test_t116_empty_buckets_ascending_dates function function test_t116_empty_buckets_ascending_dates L218–224
+ test_t117_empty_repo_ids_returns_empty_dict function async_function test_t117_empty_repo_ids_returns_empty_dict L232–237
~ tests/test_repo_card_integrity.py .py 29 symbols added
+ _HEX_RE variable variable _HEX_RE L47–47
+ _seed_two_repos function async_function _seed_two_repos L54–77
+ _utc_now function function _utc_now L43–44
+ AsyncSession import import AsyncSession L28–28
+ MusehubIntelBreakageMeta import import MusehubIntelBreakageMeta L30–30
+ MusehubIntelDead import import MusehubIntelDead L30–30
+ MusehubSymbolIntel import import MusehubSymbolIntel L30–30
+ _PULSE_DAYS import import _PULSE_DAYS L35–35
+ _SPARKLINE_HEIGHT import import _SPARKLINE_HEIGHT L35–35
+ annotations import import annotations L22–22
+ create_commit import import create_commit L40–40
+ create_repo import import create_repo L40–40
+ datetime import import datetime L25–25
+ enrich_repo_cards import import enrich_repo_cards L35–35
+ pytest import import pytest L27–27
+ re import import re L24–24
+ timedelta import import timedelta L25–25
+ timezone import import timezone L25–25
+ test_t600_pulse_bucket_count_invariant function async_function test_t600_pulse_bucket_count_invariant L85–92
+ test_t601_pulse_dates_strictly_ascending function async_function test_t601_pulse_dates_strictly_ascending L100–113
+ test_t602_pulse_counts_non_negative function async_function test_t602_pulse_counts_non_negative L121–127
+ test_t603_pulse_h_within_range function async_function test_t603_pulse_h_within_range L135–143
+ test_t604_autonomy_pct_bounded function async_function test_t604_autonomy_pct_bounded L151–158
+ test_t605_intel_counts_non_negative function async_function test_t605_intel_counts_non_negative L166–173
+ test_t606_health_status_is_valid_literal function async_function test_t606_health_status_is_valid_literal L181–189
+ test_t607_hottest_symbol_has_positive_churn function async_function test_t607_hottest_symbol_has_positive_churn L197–205
+ test_t608_blast_leader_has_positive_blast function async_function test_t608_blast_leader_has_positive_blast L213–221
+ test_t609_result_keys_match_requested_ids function async_function test_t609_result_keys_match_requested_ids L229–235
+ test_t610_pulse_bucket_colors_are_valid_hex function async_function test_t610_pulse_bucket_colors_are_valid_hex L243–248
~ tests/test_repo_card_performance.py .py 20 symbols added
+ _seed_full_repo function async_function _seed_full_repo L37–65
+ _utc_now function function _utc_now L33–34
+ AsyncSession import import AsyncSession L22–22
+ MusehubIntelBreakageMeta import import MusehubIntelBreakageMeta L24–24
+ MusehubIntelDead import import MusehubIntelDead L24–24
+ MusehubSymbolIntel import import MusehubSymbolIntel L24–24
+ annotations import import annotations L15–15
+ create_commit import import create_commit L30–30
+ create_repo import import create_repo L30–30
+ datetime import import datetime L19–19
+ enrich_repo_cards import import enrich_repo_cards L29–29
+ pytest import import pytest L21–21
+ statistics import import statistics L17–17
+ time import import time L18–18
+ timedelta import import timedelta L19–19
+ timezone import import timezone L19–19
+ test_t700_single_repo_under_100ms function async_function test_t700_single_repo_under_100ms L73–81
+ test_t701_ten_repo_batch_under_200ms function async_function test_t701_ten_repo_batch_under_200ms L89–97
+ test_t702_p95_single_repo_under_80ms function async_function test_t702_p95_single_repo_under_80ms L105–116
+ test_t703_empty_repo_faster_than_full_repo function async_function test_t703_empty_repo_faster_than_full_repo L124–149
~ tests/test_repo_card_security.py .py 17 symbols added
+ _utc_now function function _utc_now L29–30
+ AsyncSession import import AsyncSession L21–21
+ MusehubSymbolIntel import import MusehubSymbolIntel L24–24
+ annotations import import annotations L16–16
+ create_commit import import create_commit L26–26
+ create_repo import import create_repo L26–26
+ datetime import import datetime L18–18
+ enrich_repo_cards import import enrich_repo_cards L25–25
+ pytest import import pytest L20–20
+ text import import text L22–22
+ timezone import import timezone L18–18
+ test_t800_sql_injection_in_repo_id function async_function test_t800_sql_injection_in_repo_id L38–55
+ test_t801_cross_repo_data_isolation function async_function test_t801_cross_repo_data_isolation L63–84
+ test_t802_metacharacters_in_symbol_address function async_function test_t802_metacharacters_in_symbol_address L92–108
+ test_t803_extreme_numeric_values_safe function async_function test_t803_extreme_numeric_values_safe L116–135
+ test_t804_null_and_empty_agent_id_not_counted function async_function test_t804_null_and_empty_agent_id_not_counted L143–162
+ test_t805_nonexistent_repo_id_returns_zero_value function async_function test_t805_nonexistent_repo_id_returns_zero_value L170–185
~ tests/test_repo_card_state.py .py 19 symbols added
+ _utc_now function function _utc_now L35–36
+ AsyncSession import import AsyncSession L23–23
+ MusehubIntelBreakageMeta import import MusehubIntelBreakageMeta L26–26
+ MusehubIntelDead import import MusehubIntelDead L26–26
+ MusehubSymbolIntel import import MusehubSymbolIntel L26–26
+ annotations import import annotations L18–18
+ create_commit import import create_commit L32–32
+ create_repo import import create_repo L32–32
+ datetime import import datetime L20–20
+ enrich_repo_cards import import enrich_repo_cards L31–31
+ pytest import import pytest L22–22
+ text import import text L24–24
+ timezone import import timezone L20–20
+ test_t500_successive_calls_are_stable function async_function test_t500_successive_calls_are_stable L44–62
+ test_t501_new_commit_increases_pulse_total function async_function test_t501_new_commit_increases_pulse_total L70–82
+ test_t502_new_dead_symbol_increments_count function async_function test_t502_new_dead_symbol_increments_count L90–109
+ test_t503_removing_breakage_transitions_to_clean function async_function test_t503_removing_breakage_transitions_to_clean L117–140
+ test_t504_agent_commit_shifts_autonomy function async_function test_t504_agent_commit_shifts_autonomy L148–164
+ test_t505_higher_churn_replaces_hottest function async_function test_t505_higher_churn_replaces_hottest L172–189
~ tests/test_repo_card_stress.py .py 19 symbols added
+ _days_ago function function _days_ago L36–37
+ _utc_now function function _utc_now L32–33
+ AsyncSession import import AsyncSession L23–23
+ _PULSE_DAYS import import _PULSE_DAYS L25–25
+ annotations import import annotations L17–17
+ create_commit import import create_commit L29–29
+ create_repo import import create_repo L29–29
+ datetime import import datetime L20–20
+ enrich_repo_cards import import enrich_repo_cards L25–25
+ pytest import import pytest L22–22
+ time import import time L19–19
+ timedelta import import timedelta L20–20
+ timezone import import timezone L20–20
+ test_t400_no_n_plus_one_queries function async_function test_t400_no_n_plus_one_queries L45–64
+ test_t401_hundred_repos_under_two_seconds function async_function test_t401_hundred_repos_under_two_seconds L72–81
+ test_t402_high_volume_commits_correct_pulse function async_function test_t402_high_volume_commits_correct_pulse L89–106
+ test_t403_hundred_symbols_hottest_correct function async_function test_t403_hundred_symbols_hottest_correct L114–141
+ test_t404_mixed_batch_no_cross_contamination function async_function test_t404_mixed_batch_no_cross_contamination L149–176
+ test_t405_duplicate_repo_ids_idempotent function async_function test_t405_duplicate_repo_ids_idempotent L184–198
~ musehub/api/routes/musehub/ui_domains.py .py 1 symbol added, 2 symbols removed, 2 symbols modified
licenses_for_viewer_type import import licenses_for_viewer_type L24–24
new_repo_domain_page function async_function new_repo_domain_page L192–225
+ enrich_repo_cards import import enrich_repo_cards L26–26
← Older Oldest on dev
All commits
Newer → Latest on dev

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