Enriched Repo Cards — pulse, autonomy, hottest symbol, blast leader, health signal
Enriched Repo Cards — Live Intelligence at a Glance
Vision
╔══════════════════════════════════════════════════════════════════════╗
║ gabriel/musehub → ║
║ MuseHub remote server ║
║ ║
║ PULSE (30d) AUTONOMY ║
║ ▁▂▁▃▅▄▇█▆▅▄▃▂▁▂▃▄▅▆▇▆▅▄▃▂▁▂▃ ████████████████░░░░ 94% ║
║ agent / human ║
║ ║
║ 🔥 HOTTEST SYMBOL 💥 BLAST LEADER ║
║ wire_push_stream ×12 / 30d session_middleware →847 syms ║
║ ║
║ HEALTH ██████████ ● CLEAN 0 dead · 0 errors · 0 warns ║
╚══════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════╗
║ gabriel/muse → ║
║ VCS engine and CLI ║
║ ║
║ PULSE (30d) AUTONOMY ║
║ ▁▁▂▄▃▅▄▆▇█▇▆▅▄▃▄▅▆▇▆▅▄▃▅▆▇▆▅ ███████████░░░░░░░░░ 61% ║
║ ║
║ 🔥 HOTTEST SYMBOL 💥 BLAST LEADER ║
║ _genesis_hash ×8 / 30d CommitRecord →312 syms ║
║ ║
║ HEALTH ███████░░░ ● WARN 3 dead · 0 errors · 2 warns ║
╚══════════════════════════════════════════════════════════════════════╝
Data Sources (all postgres, no engine calls at render time)
| Signal | Table | Key Columns |
|---|---|---|
| Pulse sparkline | musehub_commits |
repo_id, timestamp |
| Autonomy ratio | musehub_commits |
repo_id, agent_id |
| Hottest symbol | musehub_symbol_intel |
repo_id, address, churn_30d, last_changed |
| Blast leader | musehub_symbol_intel |
repo_id, address, blast |
| Dead code count | musehub_intel_dead |
repo_id, address, confidence |
| Breakage | musehub_intel_breakage_meta |
repo_id, total_issues, error_count, warning_count |
One enrichment query per repo at page load. No per-card API calls. No engine subprocess.
Spectral Template
The card uses the --gradient-spectral palette for the pulse sparkline bars
and the autonomy bar fill. Health signal maps to semantic colors.
// Spectral palette (from :root)
// --gradient-spectral: linear-gradient(90deg, #60a5fa, #818cf8, #a78bfa, #c084fc)
// Individual stops available as named vars:
// blue → var(--color-accent) #60a5fa
// indigo → var(--color-purple) #818cf8 (alias)
// violet → var(--color-purple) #a78bfa
// pink → #c084fc (use inline or add --color-pink token)
// Sparkline bars: each bar colored by position along spectral gradient
// Bar i of N → hue interpolated from blue (#60a5fa) → violet (#a78bfa)
// Rendered as inline SVG <rect> elements; color set via fill attribute
// Autonomy bar: filled portion uses --gradient-spectral (left-to-right)
// Empty portion uses var(--bg-elevated)
// Health signal:
// CLEAN → var(--color-success) #3fb950
// WARN → var(--color-warning) #d29922
// RISK → var(--color-danger) #f87171
// Threshold: CLEAN = 0 dead + 0 errors; WARN = any dead OR warnings; RISK = any errors
Card HTML structure (target markup)
<a class="rc-card" href="/{{ repo.owner }}/{{ repo.slug }}">
<!-- Row 1: name + arrow -->
<div class="rc-card__header">
<span class="rc-card__name">{{ repo.owner }}/{{ repo.slug }}</span>
<span class="rc-card__arrow">→</span>
</div>
<!-- Row 2: description -->
{% if repo.description %}
<p class="rc-card__desc">{{ repo.description | truncate(80) }}</p>
{% endif %}
<!-- Row 3: pulse + autonomy side by side -->
<div class="rc-card__metrics">
<div class="rc-card__pulse">
<span class="rc-card__metric-label">Pulse</span>
<!-- inline SVG sparkline, 30 bars, spectral fill -->
<svg class="rc-sparkline" viewBox="0 0 120 24" preserveAspectRatio="none"
aria-label="Commit frequency last 30 days" role="img">
{% for bucket in repo.pulse_buckets %}
<rect
x="{{ loop.index0 * 4 }}" y="{{ 24 - bucket.h }}"
width="3" height="{{ bucket.h }}"
fill="{{ bucket.color }}"
rx="1"/>
{% endfor %}
</svg>
</div>
<div class="rc-card__autonomy">
<span class="rc-card__metric-label">Autonomy</span>
<div class="rc-autonomy-bar" role="meter"
aria-valuenow="{{ repo.autonomy_pct }}" aria-valuemin="0" aria-valuemax="100"
title="{{ repo.autonomy_pct }}% agent commits">
<div class="rc-autonomy-bar__fill" style="width:{{ repo.autonomy_pct }}%"></div>
</div>
<span class="rc-card__metric-val">{{ repo.autonomy_pct }}%</span>
</div>
</div>
<!-- Row 4: hottest symbol + blast leader -->
<div class="rc-card__intel">
{% if repo.hottest_symbol %}
<div class="rc-intel-item rc-intel-item--hot">
<span class="rc-intel-item__label">Hottest</span>
<code class="rc-intel-item__addr">{{ repo.hottest_symbol.name }}</code>
<span class="rc-intel-item__stat">×{{ repo.hottest_symbol.churn_30d }}</span>
</div>
{% endif %}
{% if repo.blast_leader %}
<div class="rc-intel-item rc-intel-item--blast">
<span class="rc-intel-item__label">Blast</span>
<code class="rc-intel-item__addr">{{ repo.blast_leader.name }}</code>
<span class="rc-intel-item__stat">→{{ repo.blast_leader.blast }}</span>
</div>
{% endif %}
</div>
<!-- Row 5: health signal -->
<div class="rc-card__health rc-card__health--{{ repo.health_status }}">
<span class="rc-health-dot"></span>
<span class="rc-health-label">{{ repo.health_status | upper }}</span>
<span class="rc-health-detail">
{{ repo.dead_count }} dead
· {{ repo.error_count }} errors
· {{ repo.warning_count }} warns
</span>
</div>
</a>
Implementation Phases
Phase 1 — Data layer: RepoCardEnrichment model + query
File: musehub/services/repo_card_enrichment.py
"""
repo_card_enrichment.py
=======================
Single-query enrichment service for the repo card component.
All signals are derived from existing postgres tables — no engine subprocess
calls, no per-card HTTP round trips. The service issues one bulk query per
page load (not per card) by batching repo IDs.
Signal inventory
----------------
pulse_buckets — list[PulseBucket] 30 daily commit-count buckets
autonomy_pct — int 0-100, % commits with agent_id non-null
hottest_symbol — SymbolStat | None highest churn_30d in symbol_intel
blast_leader — SymbolStat | None highest blast score in symbol_intel
dead_count — int high-confidence dead symbols
error_count — int breakage errors from breakage_meta
warning_count — int breakage warnings from breakage_meta
health_status — Literal['clean','warn','risk']
"""
Model classes:
@dataclass
class PulseBucket:
"""One day's commit count, pre-normalised to bar height (0–24px)."""
date: str # ISO date YYYY-MM-DD
count: int # raw commit count
h: int # bar height in SVG units (0–24), scaled to max in window
color: str # hex color interpolated along spectral gradient
@dataclass
class SymbolStat:
"""Compact symbol reference for card display."""
address: str # full symbol address e.g. "musehub/services/foo.py::bar"
name: str # short name — last segment after "::"
churn_30d: int # times changed in last 30 days (hottest signal)
blast: int # downstream symbol count (blast leader signal)
@dataclass
class RepoCardEnrichment:
"""
Complete enrichment payload for one repo card.
Constructed by ``enrich_repo_cards()`` in a single batched query.
All fields have safe defaults so a card always renders even if intel
tables are empty for a given repo.
"""
repo_id: str
pulse_buckets: list[PulseBucket]
autonomy_pct: int
hottest_symbol: SymbolStat | None
blast_leader: SymbolStat | None
dead_count: int
error_count: int
warning_count: int
@property
def health_status(self) -> str:
"""
Three-tier health classification.
CLEAN — zero dead symbols AND zero breakage errors.
WARN — dead symbols present OR breakage warnings present.
RISK — any breakage errors present (errors outrank warnings).
"""
if self.error_count > 0:
return "risk"
if self.dead_count > 0 or self.warning_count > 0:
return "warn"
return "clean"
Key query design:
-- Pulse: daily commit counts per repo for last 30 days
SELECT
repo_id,
DATE_TRUNC('day', timestamp)::date AS day,
COUNT(*) AS cnt
FROM musehub_commits
WHERE repo_id = ANY(:repo_ids)
AND timestamp >= NOW() - INTERVAL '30 days'
GROUP BY repo_id, day
ORDER BY repo_id, day;
-- Autonomy: agent vs human commit ratio
SELECT
repo_id,
COUNT(*) FILTER (WHERE agent_id IS NOT NULL AND agent_id != '') AS agent_commits,
COUNT(*) AS total_commits
FROM musehub_commits
WHERE repo_id = ANY(:repo_ids)
GROUP BY repo_id;
-- Hottest + blast leader from symbol_intel (one query, ranked per repo)
SELECT DISTINCT ON (repo_id)
repo_id, address, churn_30d, blast
FROM musehub_symbol_intel
WHERE repo_id = ANY(:repo_ids)
ORDER BY repo_id, churn_30d DESC NULLS LAST;
-- Dead code count
SELECT repo_id, COUNT(*) AS dead_count
FROM musehub_intel_dead
WHERE repo_id = ANY(:repo_ids)
AND confidence = 'high'
GROUP BY repo_id;
-- Breakage summary
SELECT repo_id, error_count, warning_count
FROM musehub_intel_breakage_meta
WHERE repo_id = ANY(:repo_ids);
Phase 2 — Route integration
File: musehub/api/routes/musehub/ui_domains.py
# In domain_detail_page():
enrichments = await enrich_repo_cards(db, [r["repo_id"] for r in repos_result.repos])
enrichment_map = {e.repo_id: e for e in enrichments}
# Merge into repos context
repos_ctx = []
for repo in repos_result.repos:
enc = enrichment_map.get(repo["repo_id"])
repos_ctx.append({**repo, "enrichment": enc})
Phase 3 — CSS (src/scss/components/_repo-cards.scss)
// ─────────────────────────────────────────────────────────────────────────────
// Component: rc-card (Enriched Repo Card)
//
// Used on:
// - domain detail page (/domains/@author/slug)
// - explore page (/explore)
// - profile page (/profile)
// - search results
//
// Spectral palette usage:
// pulse bars → interpolated from --color-accent to --color-purple along bar index
// autonomy fill → --gradient-spectral left-to-right
// health dot → --color-success / --color-warning / --color-danger
// ─────────────────────────────────────────────────────────────────────────────
.rc-card { ... }
.rc-sparkline { ... }
.rc-autonomy-bar { ... }
.rc-autonomy-bar__fill { background: var(--gradient-spectral); ... }
.rc-intel-item { ... }
.rc-card__health--clean .rc-health-dot { background: var(--color-success); }
.rc-card__health--warn .rc-health-dot { background: var(--color-warning); }
.rc-card__health--risk .rc-health-dot { background: var(--color-danger); }
Seven Tiers of Tests
Tier 1 — Unit
File: tests/test_repo_card_enrichment_unit.py
"""
Unit tests for RepoCardEnrichment model and pure helper functions.
T100 — health_status returns 'clean' when all counts are zero
T101 — health_status returns 'warn' when dead_count > 0, error_count == 0
T102 — health_status returns 'risk' when error_count > 0 (dominates dead+warn)
T103 — SymbolStat.name extracts last segment after '::'
T104 — PulseBucket.h is clamped to [0, 24]
T105 — autonomy_pct of 0 when total_commits == 0 (no ZeroDivisionError)
T106 — pulse_buckets always has exactly 30 entries (zero-filled for missing days)
T107 — spectral_color(0, 30) returns --color-accent hex
T108 — spectral_color(29, 30) returns --color-purple hex
T109 — enrich_repo_cards returns empty-safe RepoCardEnrichment for unknown repo_id
"""
Tier 2 — Integration
File: tests/test_repo_card_enrichment_integration.py
"""
Integration tests against a real test database.
T200 — enrich_repo_cards with a repo that has 30 days of commits returns
pulse_buckets of length 30 with correct daily counts
T201 — autonomy_pct is 100 for a repo where all commits have agent_id set
T202 — autonomy_pct is 0 for a repo where no commits have agent_id set
T203 — hottest_symbol matches the symbol with highest churn_30d in symbol_intel
T204 — blast_leader matches the symbol with highest blast score
T205 — dead_count counts only high-confidence dead symbols
T206 — health_status is 'risk' when breakage_meta has error_count > 0
T207 — health_status is 'warn' when dead symbols exist but no errors
T208 — enrich_repo_cards batches correctly — one call for N repos issues
exactly 5 SQL queries regardless of N (verified via query counter)
T209 — repos with no intel data return safe zero-value enrichment (no crash)
"""
Tier 3 — End-to-End
File: tests/test_repo_card_e2e.py
"""
End-to-end tests against the live HTTP server (httpx AsyncClient).
T300 — GET /domains/@gabriel/code renders rc-card elements for each public repo
T301 — Each rc-card contains .rc-sparkline SVG with 30 rect children
T302 — Each rc-card contains .rc-autonomy-bar with aria-valuenow attribute
T303 — Each rc-card contains .rc-card__health with data-status in {clean,warn,risk}
T304 — rc-intel-item--hot is absent when symbol_intel table is empty for repo
T305 — rc-intel-item--blast is absent when symbol_intel table is empty for repo
T306 — Card links href="/owner/slug" for each repo
T307 — Page renders < 800ms with 12 enriched cards (perf gate in e2e)
"""
Tier 4 — Stress
File: tests/test_repo_card_stress.py
"""
Stress tests for the enrichment service under load.
T400 — enrich_repo_cards with 100 repo_ids completes in < 500ms
T401 — 50 concurrent GET /domains/@gabriel/code requests all return 200
with no query failures or connection pool exhaustion
T402 — enrichment with repos having 10,000 commits each does not OOM
T403 — sparkline normalisation is stable when one bucket has 10x the
commits of all others (no bar overflow)
"""
Tier 5 — State
File: tests/test_repo_card_state.py
"""
State consistency tests — enrichment output tracks DB mutations correctly.
T500 — After inserting a new commit with agent_id set, autonomy_pct increases
T501 — After inserting a new commit, pulse_buckets[today].count increments
T502 — After inserting a high-confidence dead symbol, health_status degrades
from 'clean' to 'warn'
T503 — After inserting a breakage error row, health_status degrades to 'risk'
T504 — After deleting all dead symbols, health_status recovers to 'clean'
T505 — hottest_symbol updates when a new symbol_intel row has higher churn_30d
"""
Tier 6 — Integrity
File: tests/test_repo_card_integrity.py
"""
Data integrity tests — enrichment never surfaces data from the wrong repo.
T600 — pulse_buckets for repo A contains no commits from repo B
T601 — hottest_symbol.address is always prefixed with a path belonging to the
queried repo (no cross-repo symbol leakage)
T602 — autonomy_pct for a repo with 0 commits is exactly 0 (not inherited)
T603 — dead_count for repo A is unaffected by dead symbols in repo B
T604 — enrich_repo_cards([]) returns [] without querying any table
T605 — blast_leader.blast is always a non-negative integer (no sentinel -1)
"""
Tier 7 — Performance
File: tests/test_repo_card_performance.py
"""
Performance regression tests — query plan and timing gates.
T700 — EXPLAIN ANALYZE on the pulse query shows index scan on
(repo_id, timestamp) — no sequential scan on musehub_commits
T701 — EXPLAIN ANALYZE on the symbol_intel query shows DISTINCT ON
uses the (repo_id, churn_30d) index
T702 — Total enrichment wall time for 12 repos is < 100ms at p95
(measured over 100 iterations in a warm-cache scenario)
T703 — Memory footprint of enrich_repo_cards(12 repos × 30 buckets)
does not exceed 2MB (tracemalloc gate)
T704 — Adding a covering index on musehub_commits(repo_id, timestamp, agent_id)
reduces autonomy query cost by > 50% vs table scan (EXPLAIN diff)
"""
Security
File: tests/test_repo_card_security.py
"""
Security tests — enrichment cannot be used to leak private repo data.
T800 — enrich_repo_cards only returns data for repo_ids explicitly passed in;
passing a private repo_id for another user returns zero-value enrichment
(service layer must respect visibility filtering done by the caller)
T801 — symbol addresses in hottest_symbol and blast_leader are HTML-escaped
when rendered in the template (no XSS via crafted symbol names)
T802 — pulse_buckets.color values are hex strings matching /^#[0-9a-f]{6}$/i;
no user-controlled content flows into SVG fill attributes
T803 — repo_id inputs to enrich_repo_cards are validated as sha256: prefixed
strings before being interpolated into SQL (parameterised query audit)
T804 — A repo with visibility='private' does not appear in domain card listing
even if its repo_id is known (enforced at list_repos_for_domain layer)
"""
Indexes Required
-- Pulse query — already has (repo_id, timestamp) but add agent_id for autonomy
CREATE INDEX CONCURRENTLY IF NOT EXISTS
ix_musehub_commits_repo_ts_agent
ON musehub_commits (repo_id, timestamp DESC, agent_id);
-- Symbol intel — covering index for card queries
CREATE INDEX CONCURRENTLY IF NOT EXISTS
ix_musehub_symbol_intel_repo_churn
ON musehub_symbol_intel (repo_id, churn_30d DESC NULLS LAST);
CREATE INDEX CONCURRENTLY IF NOT EXISTS
ix_musehub_symbol_intel_repo_blast
ON musehub_symbol_intel (repo_id, blast DESC NULLS LAST);
Reuse Contract
The rc-card component and RepoCardEnrichment service are designed for
reuse across all repo listing surfaces:
/domains/@author/slug— domain detail (this ticket)/explore— global repo discovery/@author— profile page/search— search results
Any page that renders a list of repos should call enrich_repo_cards() and
pass the result into the template as enrichment_map. The card partial will
be extracted to musehub/templates/musehub/partials/repo_card.html.
All deliverables complete and landed to main.
Phase 1 —
enrich_repo_cards()service with all 5 signals (pulse, autonomy, hottest, blast, dead/breakage) in 5 batch queries regardless of repo count.Phase 2 — Wired into all four repo listing surfaces: domain detail, explore, profile, and search.
Phase 3 —
_repo_cards.scsswithrc-grid,rc-card, sparkline, autonomy bar, donut health gauge, and intel counts.Tests — T100–T805 across 8 tiers (unit, integration, e2e, stress, state, integrity, performance, security), all passing.
Indexes —
ix_symbol_intel_repo_churn(existing) +ix_symbol_intel_repo_blast(migration 0033) covering both DISTINCT ON queries.