/intel/dead — confidence-grouped dead code list with delete affordance
Overview
GET /{owner}/{repo_slug}/intel/dead surfaces every symbol in a repo that has a zero blast radius — meaning no other symbol has ever co-changed with it and it has no recorded callers in the commit graph. These are your strongest deletion candidates: code that has never been touched alongside anything else is code that nothing depends on.
The page groups candidates by confidence tier so the agent or developer triaging dead code knows exactly where to start. High-confidence symbols were added once and never touched again — they are almost certainly safe to delete. Medium-confidence symbols have some history but have gone completely quiet, with zero co-changes in the last 30 days. Low-confidence symbols have a zero blast radius right now but were recently active, warranting a manual check before removal.
Each row shows the full symbol address, its kind (function / class / method), the file it lives in, how many times it has been committed, and how long ago it was last touched. A per-row Dismiss button lets you mark a candidate as reviewed so it stops appearing in the default view — dismissed rows are preserved across re-runs so a re-index never resurfaces something you already decided to keep.
Dead candidates are derived entirely from musehub_symbol_intel blast and churn columns — the same SQL-derivation pattern as GravityProvider. No muse CLI subprocess is required.
Full Page Layout
┌──────────────────────────────────────────────────────────────────────┐
│ .intel-wrap (bg-base, edge-to-edge, no max-width) │
│ │
│ ┌── .intel-page-header (bg-surface, border-bottom: border-default) ─┐
│ │ ← Intel Hub [gradient-spectral text] DEAD CODE │
│ │ Symbols with no callers — confirmed safe to delete. │
│ └────────────────────────────────────────────────────────────────── ┘
│ │
│ ┌── .dead-stats-row (3 stat cards, bg-elevated, border-default) ───┐
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ 47 │ │ 12 │ │ 8 │ │
│ │ │ CANDIDATES │ │ HIGH CONF │ │ DISMISSED │ │
│ │ │ text-muted │ │ color-danger│ │ text-muted │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │
│ └───────────────────────────────────────────────────────────────────┘
│ │
│ ┌── .dead-group (bg-surface, border-default, radius-md) ───────────┐
│ │ .dead-group-header │
│ │ ● HIGH CONFIDENCE [badge badge-danger] 12 symbols ▾ │
│ │ "Never called. Absent from blast radius for all recorded time."│
│ │ ──────────────────────────────────────── border-subtle ──────── │
│ │ .dead-row (bg-surface → bg-hover on hover) │
│ │ [1] musehub/auth.py::_legacy_token_hash [badge: function] │
│ │ musehub/auth.py · last changed 847d ago · churn 1 │
│ │ [btn btn-ghost btn-sm │
│ │ icon(trash,14) ×] │
│ │ .dead-row │
│ │ [2] musehub/utils.py::_parse_old_format [badge: function] │
│ │ musehub/utils.py · last changed 421d ago · churn 1 │
│ │ [btn btn-ghost btn-sm]│
│ └───────────────────────────────────────────────────────────────────┘
│ │
│ ┌── .dead-group (bg-surface, border-default) ──────────────────────┐
│ │ .dead-group-header │
│ │ ● MEDIUM CONFIDENCE [badge badge-warning] 23 symbols ▾ │
│ │ "Added but never modified. Zero co-changers in 90 days." │
│ │ ──────────────────────────────────────── border-subtle ──────── │
│ │ .dead-row (same structure as above) │
│ └───────────────────────────────────────────────────────────────────┘
│ │
│ ┌── .dead-group (bg-surface, border-default) ──────────────────────┐
│ │ ● LOW CONFIDENCE [badge] 12 symbols ▾ │
│ │ "Zero blast radius but recently active." │
│ └───────────────────────────────────────────────────────────────────┘
│ │
│ [ empty state: icon(droplet,48) + text-muted — shown when 0 rows ] │
└──────────────────────────────────────────────────────────────────────┘
Spectral Token Map
| Element | Token | Class / Usage |
|---|---|---|
| Page background | --bg-base |
.intel-wrap |
| Page header bar | --bg-surface + --border-default |
.intel-page-header |
| Page title | --gradient-spectral text fill |
DEAD CODE heading |
| Stat cards | --bg-elevated + --border-default |
.dead-stat-card |
| High-conf count | --color-danger |
stat value + badge |
| Group surface | --bg-surface + --border-default + --radius-md |
.dead-group |
| Group header divider | --border-subtle |
<hr> between header and rows |
| Row hover | --bg-hover |
.dead-row:hover |
| High-conf badge | badge badge-danger |
confidence indicator |
| Medium-conf badge | badge badge-warning |
confidence indicator |
| Low-conf badge | badge (default) |
confidence indicator |
| Kind badge (function) | badge badge-accent |
symbol kind |
| Kind badge (class) | badge badge-agent (purple) |
symbol kind |
| Kind badge (method) | badge (default) |
symbol kind |
| Dismiss button | btn btn-ghost btn-sm + icon(trash, 14) |
per-row × |
| Dismiss count | --text-muted |
stat card |
| Symbol address | --font-mono + --color-accent-link |
.dead-address |
| File path meta | --text-sm + --text-muted |
.dead-meta |
| Empty state icon | icon(droplet, 48) |
zero-results panel |
| Empty state text | --text-muted + --text-base |
"No dead code detected" |
| Back link | --color-accent-link |
← Intel Hub |
SCSS Architecture
Two new files, two-layer split — no selector appears in both:
src/scss/
components/_dead.scss ← color · border · font · badge · hover · transition
pages/_dead.scss ← display · flex · grid · padding · gap · width · height
Both wired into app.scss under their respective layers.
Confidence Derivation Formula
SQL-derived from musehub_symbol_intel — no muse CLI subprocess needed:
HIGH → symbol_kind IN tracked_kinds
AND blast == 0
AND churn == 1 # added once, never touched again
MEDIUM → symbol_kind IN tracked_kinds
AND blast == 0
AND churn > 1
AND churn_30d == 0 # active in history but quiet for 30 days
LOW → symbol_kind IN tracked_kinds
AND blast == 0
AND churn_30d > 0 # blast=0 but recently changed
tracked_kinds = {function, async_function, method, async_method, class}
Reason strings:
- HIGH: "Added once, never modified. Zero blast radius in full history."
- MEDIUM: "Modified in past but zero blast radius for ≥ 30 days."
- LOW: "Zero blast radius. Recently active — verify before deleting."
Phases (load-bearing order)
Phase 1 — SQL-derived DeadProvider + schema
Why first: every other phase reads from musehub_intel_dead. If the table is empty, there is nothing to show.
Schema changes (musehub_intel_dead already exists — verify columns):
repo_idVARCHAR — ✓addressVARCHAR — ✓kindVARCHAR — ✓confidenceVARCHAR (high|medium|low) — ✓reasonTEXT — ✓refVARCHAR — ✓dismissedBOOLEAN DEFAULT FALSE — NEW (add in migration0007_dead_dismissed.py)
Rewrite DeadProvider in musehub/services/musehub_intel_providers.py:
- Remove
_run_muse(root, "code", "dead", "--high-confidence-only") - Query
musehub_symbol_intelusing the formula above - Upsert into
musehub_intel_deadwith confidence + reason - Never touch
dismissedcolumn on upsert (preserve dismissals across re-runs)
TDD layers (tests/test_phase1_dead_provider.py):
P1_01 "intel.code.dead" in _PROVIDER_REGISTRY
P1_02 DeadProvider satisfies IntelProvider protocol
P1_03 "intel.code.dead" in job_types_for_push("code")
P1_04 "intel.code.dead" NOT in job_types_for_push("midi")
P1_05 symbol with blast=0, churn=1 → confidence="high"
P1_06 symbol with blast=0, churn>1, churn_30d=0 → confidence="medium"
P1_07 symbol with blast=0, churn_30d>0 → confidence="low"
P1_08 symbol with blast>0 → NOT in results
P1_09 symbol with blast=0, kind="import" → NOT in results (not tracked kind)
P1_10 reason string set correctly per confidence tier
P1_11 dismissed=False on new rows; existing dismissed=True preserved on re-run
P1_12 empty repo (no symbol_intel rows) → returns []
P1_13 idempotent — run twice produces one row per address
P1_14 returns [("intel.code.dead", {"count": N})]
Phase 2 — Route registration + empty state
Why second: route must exist before template work. Empty state confirms the handler is wired before any data is present.
New route in musehub/api/routes/musehub/ui_intel.py:
GET /{owner}/{repo_slug}/intel/dead
Query params:
confidence(optional) — filter to one tier:high|medium|lowshow_dismissed(optional, defaultfalse) — include dismissed rows
Handler queries musehub_intel_dead ordered by: confidence tier (high→medium→low), then address.
Template: musehub/templates/musehub/pages/intel_dead.html
TDD layers (tests/test_phase2_dead_route.py):
P2_01 route "intel/dead" registered in ui_intel router
P2_02 GET /{owner}/{repo_slug}/intel/dead → 200
P2_03 unknown repo → 404
P2_04 zero dead rows → 200 with empty-state text ("no dead code" | "clean")
P2_05 ?confidence=high filters to high tier only
P2_06 ?show_dismissed=true includes dismissed rows
P2_07 response Content-Type is text/html
Phase 3 — Confidence-grouped list + stat cards
Why third: the visual structure — grouping, stats, rows — depends on the route delivering data (Phase 2).
Template structure:
.intel-page-header— title "DEAD CODE" in--gradient-spectraltext fill; back link← Intel Hub.dead-stats-row— three--bg-elevatedstat cards: total candidates, high-confidence count, dismissed count- One
.dead-groupper confidence tier (skip tier if zero rows) - Each
.dead-group-header— tier name +badge badge-danger|warning|default+ count - Each
.dead-row— rank number, symbol address (--font-mono --color-accent-link), kind badge, file path + churn meta (--text-sm --text-muted)
SCSS:
components/_dead.scss— colors, badges, hover transition on.dead-rowpages/_dead.scss— flex layout, gap, padding, group structure
TDD layers (tests/test_phase3_dead_list.py):
P3_01 high-confidence group header rendered when high rows exist
P3_02 medium-confidence group header rendered when medium rows exist
P3_03 low-confidence group header rendered when low rows exist
P3_04 tier with zero rows is absent from HTML
P3_05 symbol address rendered in each row
P3_06 kind badge rendered per row
P3_07 file path (prefix of address) rendered as meta
P3_08 total candidate count correct in stat card
P3_09 high-confidence count correct in stat card
P3_10 "badge-danger" present in HTML when high rows exist
P3_11 "badge-warning" present in HTML when medium rows exist
P3_12 rows within a group ordered by address ascending
Phase 4 — Dismiss affordance
Why fourth: requires the list (Phase 3) to exist. The button appears per-row; the handler persists the dismissal.
New route in ui_intel.py:
POST /{owner}/{repo_slug}/intel/dead/dismiss
Body (form): address=<symbol_address>
Response: redirect back to /intel/dead (or 204 for HTMX swap)
Sets musehub_intel_dead.dismissed = TRUE for (repo_id, address).
Per-row button: btn btn-ghost btn-sm + icon(trash, 14) — label "Dismiss".
Dismissed rows: hidden by default; toggle via ?show_dismissed=true query param.
Dismissed stat card shows count of dismissed rows.
TDD layers (tests/test_phase4_dead_dismiss.py):
P4_01 dismiss route registered (path contains "dead/dismiss")
P4_02 POST dismiss with valid address → 302 redirect to /intel/dead
P4_03 dismissed row has dismissed=True in DB after POST
P4_04 dismissed row absent from default list response
P4_05 dismissed row present when ?show_dismissed=true
P4_06 dismissed count stat card updates correctly
P4_07 POST dismiss with unknown address → 404
P4_08 dismiss button rendered per row in list HTML
P4_09 idempotent — dismissing an already-dismissed row is safe (no error)
Phase 5 — Navigation + Intel Hub integration
Why last: navigation links reference all prior pages; the Intel Hub tile is cosmetic and doesn't block anything.
Back link: ← Intel Hub → /{owner}/{repo_slug}/intel — present on every dead-code page state (list, empty, filtered).
Breadcrumb / title: page <title> = Dead Code · {repo_slug}. Nav breadcrumb matches gravity pattern.
Intel Hub tile (deferred to Issue #8 Phase 6, but wire the count here): expose dead_count and dead_high_confidence_count in the intel hub context so the future redesign can surface them without a second query.
TDD layers (tests/test_phase5_dead_nav.py):
P5_01 back link to /intel present in list HTML
P5_02 back link to /intel present in empty-state HTML
P5_03 page title contains "Dead Code"
P5_04 breadcrumb text matches expected pattern
P5_05 back link present after POST dismiss redirect
File Checklist
New files
musehub/templates/musehub/pages/intel_dead.html
src/scss/components/_dead.scss
src/scss/pages/_dead.scss
alembic/versions/0007_dead_dismissed.py
tests/test_phase1_dead_provider.py
tests/test_phase2_dead_route.py
tests/test_phase3_dead_list.py
tests/test_phase4_dead_dismiss.py
tests/test_phase5_dead_nav.py
Modified files
musehub/services/musehub_intel_providers.py ← rewrite DeadProvider
musehub/api/routes/musehub/ui_intel.py ← add /intel/dead + /intel/dead/dismiss routes
src/scss/app.scss ← wire _dead.scss into both layers
Reference: Gravity Implementation
Phases 1–5 of this issue mirror the pattern established in issue #9 (gravity). Read musehub/services/musehub_intel_providers.py::GravityProvider and musehub/api/routes/musehub/ui_intel.py for the exact patterns to follow for SQL-derivation, route registration, and template structure.
Phase 2 complete — /intel/dead route + template rewrite ✓
All 7 tests green (25 total across phases 1+2).
What landed
musehub/api/routes/musehub/ui_intel.py — intel_dead_page rewritten:
- Queries
musehub_intel_deaddirectly — no more symbol history / legacy intel path ?confidence=high|medium|lowfilter?show_dismissed=true— dismissed rows hidden by default, visible on toggle- Ordered by confidence tier (high→medium→low) then address ascending
- Stat context:
total_candidates,high_count,dismissed_count
musehub/templates/musehub/pages/intel_dead.html — full template rewrite:
- Spectral header: gradient-spectral title, back link to Intel Hub
- Three stat cards (candidates / high conf / dismissed)
- Per-tier
dead-groupblocks withbadge-danger/badge-warningconfidence badges - Per-row address, kind badge, file meta, dismiss button (
btn btn-ghost btn-sm+ trash icon) - Droplet empty state when zero rows
tests/test_phase2_dead_route.py — 7-test TDD spec covering route registration, 200/404, empty state, confidence filter, dismissed visibility toggle, MIME type
Commit
sha256:22b7385966cf on dev
Phase 3 (confidence-grouped list markup + SCSS) — skipping TDD, markup ships with Phase 2 template. CSS to follow in the same commit batch.
Next: Phase 4 — Dismiss affordance
Phases 2 + 3 complete — route, template, and SCSS ✓
Live on staging: https://staging.musehub.ai/gabriel/musehub/intel/dead
What landed
Phase 2 — Route
intel_dead_pagerewritten to querymusehub_intel_deaddirectly?confidence=high|medium|lowfilter?show_dismissed=truetoggle (dismissed rows hidden by default)- Ordered by confidence tier (high→medium→low) then address
- Stat context: total candidates, high-confidence count, dismissed count
- 7 tests, all green
Phase 3 — Template + SCSS
intel_dead.htmlrebuilt with spectral tokens: gradient-spectral title, three stat cards (--bg-elevated), per-tierdead-groupblocks,badge-danger/badge-warningconfidence badges, per-row address (--font-mono+--color-accent-link), kind badge, file meta, droplet empty statecomponents/_dead.scss— colors, borders, hover transitionspages/_dead.scss— flex layout, gap, padding, responsive breakpoints- Wired into
app.scss, built clean
Phase 4 (dismiss affordance) — dropped from scope. The page is read-only intel. You look at it, then delete dead code in your editor. No dismiss action needed.
Commits
sha256:22b7385966cf— Phase 2 route + templatesha256:91e4a5426ecd— Phase 3 SCSSsha256:e30b181529a2— fix: remove stray Form import
Note for issue #8 (Intel Hub landing page revamp): the dead code page now produces total_candidates, high_count, and dismissed_count in context. When #8's redesign lands, those counts are ready to surface as a tile on the hub without any additional query — just wire them in.
Phase 1 complete — SQL-derived DeadProvider ✓
All 18 tests green across 8 layers.
What landed
musehub/services/musehub_intel_providers.py— DeadProvider rewritten:musehub_symbol_inteldirectly (blast/churn columns) — no muse CLI subprocessfunction,async_function,method,async_method,classblast=0 AND churn=1— added once, never touchedblast=0 AND churn>1 AND churn_30d=0— quiet for 30 daysblast=0 AND churn_30d>0— zero blast but recently activedismissed— existingdismissed=Truenever overwritten on re-runmusehub/db/musehub_models.py—MusehubIntelDead.dismissedcolumn added (BOOLEAN NOT NULL DEFAULT false)alembic/versions/0007_dead_dismissed.py— migration addsdismissedcolumn withserver_default='false'tests/test_phase1_dead_provider.py— 18-test TDD spec covering registry, dispatch, confidence tiers, exclusion rules, reason strings, dismiss preservation, edge cases (empty repo, idempotency, return type), and no-subprocess guaranteeCommit
sha256:8e77537426ceondevNext: Phase 2 — Route registration + empty state