gabriel / musehub public
Open #25 Enhancement
filed by gabriel human · 39 days ago

Symbols Page v2 — Symbol-First Intelligence Browser

0 Anchors
Blast radius
Churn 30d
0 Proposals

Symbols Page v2 — Symbol-First Intelligence Browser

Status: Planning Assignee: gabriel Priority: P0 — primary navigation surface for symbol intelligence


Revised Vision

The Symbols page is the symbol browser — not a repo dashboard.

The Intel page (/intel) already owns repo-level health: health gauge, alert strip, hotspot/dead/blast-risk cards. Building a second bento dashboard on /symbols duplicates that entirely and splits the user's mental model.

The right question /symbols answers:

"What symbols exist in this repo and which ones should I care about right now?"

Every row in the symbol table IS the dashboard — for that symbol. The page becomes a data-dense, keyboard-navigable, filterable table where each row surfaces the complete signal picture for one symbol without a click.

Intel = repo-first (metric → symbols). Symbols = symbol-first (browse → detail). These must not overlap.


What Changes

REMOVE from scope (already covered by /intel):
  ✗  6-panel bento dashboard (health score, churn chart, gravity map,
     dead-code panel, kind donut, author leaderboard)
  ✗  musehub_repo_dashboard pre-compute table
  ✗  Repo-level vitals hero duplication

KEEP — what already exists and is good:
  ✓  Hero banner (compact stat strip: total, hotspots, dead, blast risk)
  ✓  Live-filter search input
  ✓  Op/kind filter pills
  ✓  Cursor pagination

ADD — the actual v2 work:
  ✓  Per-row sparkline (change frequency mini-chart, last 30 commits)
  ✓  Per-row coupling score (# symbols coupled to, from pre-computed table)
  ✓  Per-row blast indicator (downstream symbol count)
  ✓  Per-row age label (first_introduced relative, from symbol_vitals)
  ✓  Keyboard navigation — j/k to move, Enter to open detail, / to focus search
  ✓  Spectral row highlight on hover (border-glow effect)
  ✓  Signal chips inline: HOTSPOT · DEAD · HIGH-BLAST (vs separate column)
  ✓  Kind filter remembers state in URL param
  ✓  Empty-state narrative for new repos

Row Design (the core of v2)

┌─────────────────────────────────────────────────────────────────────────────┐
│  fn  musehub/services/musehub_wire.py::_to_wire_commit          HOTSPOT     │
│                                                                              │
│  ▁▃▅▇▅▃▁▂▄▆▅▂  14×   coupled: 8   blast: 23   born 4mo ago    ↗ 2.1×/wk  │
└─────────────────────────────────────────────────────────────────────────────┘
  kind  address (full, ellipsized)                          signal chips

  sparkline  change_count  coupling_count  blast_count  age  velocity

All values from pre-computed tables (musehub_symbol_vitals, musehub_symbol_coupling) — zero aggregation at request time.


Data Already Available (from Phase 0 / fast-reads work)

musehub_symbol_vitals     — first_introduced, change_count, version_count,
                            op breakdown, avg_velocity (can compute from count + age)
musehub_symbol_coupling   — shared_commits per co_address → COUNT = coupling_score
musehub_symbol_history_entries — message, commit_branch (denormalized)

The coupling count per symbol needs one extra pre-computed column: coupling_count INT on musehub_symbol_vitals — incremented at push time.

Blast radius count (# symbols this one co-changes with) = same as coupling_count.


Implementation Phases (load-bearing order)

Phase 1 — Data: coupling_count on vitals (½ day)

┌─────────────────────────────────────────────┐
│  musehub_symbol_vitals                      │
│  + coupling_count INT DEFAULT 0             │
│                                             │
│  Indexer: after _upsert_symbol_coupling()   │
│    → UPDATE vitals SET coupling_count =     │
│         (SELECT COUNT(*) FROM coupling      │
│          WHERE repo_id=? AND address=?)     │
└─────────────────────────────────────────────┘

Migration: 0032_symbol_vitals_coupling_count.py Tests: V101–V105 (column exists, populated, accurate, idempotent, cascade)

Phase 2 — Route: enrich symbol list rows (½ day)

symbol_list_page() currently returns:
  address, kind, op, committed_at, heat (change_count from entries)

Add from vitals JOIN:
  first_introduced, change_count (authoritative), coupling_count,
  version_count, op breakdown

SQL: LEFT JOIN musehub_symbol_vitals ON (repo_id, address)
No extra queries — one join, same pagination.

Tests: R201–R205 (row includes vitals fields, null-safe, paginated correctly)

Phase 3 — Template: data-dense rows (1 day)

┌──────────────────────────────────────────────────────────┐
│ SYMBOL TABLE v2                                          │
│                                                          │
│ [/] search...  [all ▾] [fn] [cls] [file]  [hotspot only] │
│                                                          │
│ ▸ fn   path/to/file.py::symbol_name       HOTSPOT        │
│        ▁▂▄▇▄▂▁  14×  coupled:8  4mo  2.1×/wk            │
│                                                          │
│ ▸ cls  path/to/other.py::ClassName                       │
│        ▁▁▁▂▁▁▁   3×  coupled:2  1yr  0.1×/wk            │
│                                                          │
│   < prev   1–50 of 847   next >                          │
└──────────────────────────────────────────────────────────┘

Spectral hover: row gets --border-glow + spectral-text on address
Keyboard:  j/k = move focus  Enter = open detail  / = search

CSS: .sym2-row--v2, .sym2-sparkline, .sym2-coupling-badge No JS framework — vanilla <script> block, keyboard listener on table.

Tests: T301–T308 (template renders vitals, sparkline data attr, keyboard attrs present, signal chips, spectral class applied, pagination)

Phase 4 — Spectral Polish (½ day)

• Focused row: spectral-glow border left edge (4px, gradient)
• Signal chips: HOTSPOT=amber-glow, DEAD=frost-glow, HIGH-BLAST=crimson-glow
• Search input: spectral focus ring on :focus
• Stat strip: subtle spectral gradient underline (not a full bento panel)
• Zero layout changes to /intel — it stays as-is

Tests: T401–T403 (CSS classes present in rendered HTML)

Phase 5 — Empty State + Onboarding (¼ day)

┌─────────────────────────────────────────────┐
│                                             │
│   ◈  No symbols indexed yet                 │
│                                             │
│   Push your first commit to build the       │
│   symbol index. Every function, class,      │
│   and file will appear here with full       │
│   provenance — automatically.               │
│                                             │
│   [ View docs → ]                           │
│                                             │
└─────────────────────────────────────────────┘

Tests: T501–T502 (empty state shown when index_status=not_built, hidden otherwise)


Seven-Tier Test Coverage

Tier 1 — DB schema       V101–V105  (vitals coupling_count column)
Tier 2 — Indexer unit    V201–V205  (coupling_count populated correctly)
Tier 3 — Route unit      R201–R210  (list endpoint returns vitals join data)
Tier 4 — Template        T301–T315  (row renders all data-dense fields)
Tier 5 — Integration     I401–I410  (push → index → list shows coupling_count)
Tier 6 — Accessibility   A501–A505  (keyboard nav attrs, aria-labels)
Tier 7 — Visual/E2E      E601–E605  (spectral classes, empty state)

Total: ~45 test IDs across 3 test files.


Muse Intel Pre-Read (before starting each phase)

muse -C ~/ecosystem/musehub code grep "symbol_list_page" --json
muse -C ~/ecosystem/musehub code impact "musehub/api/routes/musehub/ui_symbols.py::symbol_list_page" --json
muse -C ~/ecosystem/musehub code deps "musehub/api/routes/musehub/ui_symbols.py" --json
muse -C ~/ecosystem/musehub code hotspots --top 5 --json

Branch Strategy

muse checkout -b feat/symbols-v2-p1-coupling-count --intent "add coupling_count to vitals" --resumable
muse checkout -b feat/symbols-v2-p2-route-join     --intent "enrich list rows with vitals join" --resumable
muse checkout -b feat/symbols-v2-p3-template       --intent "data-dense symbol rows" --resumable
muse checkout -b feat/symbols-v2-p4-spectral       --intent "spectral polish on symbol table" --resumable
muse checkout -b feat/symbols-v2-p5-empty-state    --intent "empty state onboarding" --resumable

Acceptance Criteria

  • Each symbol row shows: sparkline, change_count, coupling_count, age, velocity
  • j/k keyboard navigation works, Enter opens detail, / focuses search
  • Spectral glow on focused row (no layout shift)
  • /intel page is untouched — zero overlap
  • All ~45 tests pass
  • Symbol list page loads in <200ms (single JOIN, no aggregation)
  • Empty state renders correctly for un-indexed repos
Activity
gabriel opened this issue 39 days ago
No activity yet. Use the CLI to comment.