Issues List: Codebase Intelligence Mission Control
Issues List: Codebase Intelligence Mission Control
What a Muse Issue Actually Is
Before redesigning the list, we have to be clear about what we're listing.
A GitHub issue is a prose message in a ticketing system. A human types a description of a problem, other humans reply, and someone eventually closes it with a comment.
A Muse issue is a named coordination point in the codebase's evolution. It carries:
symbol_anchors— specific named symbols (file.py::Symbol) not line numberscommit_anchors— VCS commit IDs that directly address this issueagent_id+model_id— cryptographic agent provenance when filed by an AI- Live code intelligence via
musehub_symbol_indexer: blast radius, churn frequency, dead-code flags, co-change clusters - Three proposal resolution strategies: commit graph ancestry, branch reachability, symbol overlap
- A release context: which Muse release a fix actually landed in
- A typed event timeline:
opened | closed | labeled | symbol_anchored | proposal_linked | proposal_merged
The list page must communicate ALL of this in compressed, scannable form. A row that shows only title + author + date is a GitHub clone. A row that shows title + symbol heat + blast exposure + proposal coverage + agent provenance is a Muse issue.
The List as Codebase Health Dashboard
The issues list is not a to-do list. It is a real-time health dashboard of the codebase's known problems, organised by code intelligence signals rather than creation date.
╔══════════════════════════════════════════════════════════════════════════════════╗
║ ISSUES MISSION CONTROL · gabriel/muse ║
╠══════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌── HEALTH PULSE ────────────────────────────────────────────────────────────┐ ║
║ │ ●OPEN 14 ●CLOSED 47 ⚠ 3 HOTSPOT 🔴 2 BLAST-CRITICAL 🤖 5 AGENT │ ║
║ │ ──────────────────────────────────────────────────────────────────────── │ ║
║ │ SYMBOL HEAT (open issues): │ ║
║ │ compute_snapshot_id ████████████ 8 issues churn: 12× blast: 47 │ ║
║ │ write_commit ██████░░░░░░ 4 issues churn: 8× blast: 31 │ ║
║ │ _replay_branch ████░░░░░░░░ 2 issues churn: 3× blast: 12 │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌── FILTERS ─────────────────────────────────────────────────────────────────┐ ║
║ │ STATE: [open ▾] LABEL: [▾] SYMBOL: [▾] AUTHOR: [▾] SORT: [▾] │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
╠══════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌── ROW: HOTSPOT ISSUE ──────────────────────────────────────────────────────┐ ║
║ │ 🔴 #42 [bug] [performance] ◈ compute_snapshot_id │ ║
║ │ Memory leak in snapshot diffing on large repos │ ║
║ │ │ ║
║ │ blast: 47 churn: 12× ✗ no tests ✗ no proposal @agentception-w03 │ ║
║ │ ─ [AGENT: claude-opus-4-6] · 2 days ago · 3 anchors · 0 comments │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌── ROW: PROPOSAL-COVERED ───────────────────────────────────────────────────┐ ║
║ │ 🟡 #38 [enhancement] ◈ write_commit ◈ CommitRecord │ ║
║ │ Deterministic commit IDs break on timezone-naïve datetimes │ ║
║ │ │ ║
║ │ blast: 31 churn: 8× ✓ tests ✅ proposal #7 (open) @gabriel │ ║
║ │ ─ [human] · 5 days ago · 2 anchors · 3 comments │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌── ROW: UNANCHORED (needs triage) ──────────────────────────────────────────┐ ║
║ │ ⚪ #35 [question] ─ no symbol anchors │ ║
║ │ Muse push fails silently when remote is unreachable │ ║
║ │ │ ║
║ │ blast: — churn: — ? needs anchoring no proposal @aaronrene │ ║
║ │ ─ [human] · 1 week ago · 0 anchors · 1 comment │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
║ ┌── ROW: RELEASED (already fixed) ───────────────────────────────────────────┐ ║
║ │ 🟢 #29 [bug] ◈ _CatFile.read │ ║
║ │ cat-file process leaks stdin handle after KeyboardInterrupt │ ║
║ │ │ ║
║ │ blast: 12 ✓ tests ✅ merged proposal #4 📦 v0.9.2 @gabriel │ ║
║ │ ─ [human] · 2 weeks ago · 1 anchor · 2 comments · CLOSED │ ║
║ └────────────────────────────────────────────────────────────────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════════════╝
ASCII Layout Art
Layout A — Symbol Heat Header (always visible, no filter required)
╔══════════════════════════════════════════════════════════════════════════════╗
║ CODEBASE SYMBOL HEAT (14 open issues across 7 symbols) ║
║ ║
║ compute_snapshot_id ██████████████████████░░ 8 issues churn 12× B:47 ║
║ write_commit ████████████░░░░░░░░░░░░ 4 issues churn 8× B:31 ║
║ _replay_branch ████████░░░░░░░░░░░░░░░░ 2 issues churn 3× B:12 ║
║ canonical_message ████░░░░░░░░░░░░░░░░░░░░ 1 issue churn 5× B:18 ║
║ (no anchor) ██░░░░░░░░░░░░░░░░░░░░░░ 3 issues — — ║
║ ║
║ B = blast radius (direct + cross-repo callers) ║
╚══════════════════════════════════════════════════════════════════════════════╝
Layout B — Full Issue Row (annotated)
┌─────────────────────────────────────────────────────────────────────────────┐
│ [STATE] #NUM [labels…] [PRIMARY ANCHOR SYMBOL] │
│ Issue title here — truncated at ~80 chars if needed… │
│ │
│ blast: N churn: N× [✓/✗ tests] [✅/✗ proposal] [📦 release] │
│ ─ [human|AGENT: model] · relative_time · N anchors · N comments │
└─────────────────────────────────────────────────────────────────────────────┘
State indicators:
- 🔴 Open + high blast/churn (hotspot) — critical attention needed
- 🟡 Open + proposal exists — being worked
- ⚪ Open + no anchors — needs triage
- 🟢 Open + merged proposal OR release — may already be fixed
- ● Closed — resolved
Layout C — Compact Mobile Row
╔═══════════════════════════════════════════════╗
║ ●14 OPEN ⚠3 HOT 🤖5 AGENT ●47 CLOSED ║
╠═══════════════════════════════════════════════╣
║ 🔴 #42 [bug] Memory leak in snapshot… ║
║ ◈ compute_snapshot_id B:47 churn:12× ║
║ ✗proposal @agentception-w03 2d 🤖 ║
╠═══════════════════════════════════════════════╣
║ 🟡 #38 [enhancement] Deterministic… ║
║ ◈ write_commit B:31 ✅ PR#7 @gabriel ║
╠═══════════════════════════════════════════════╣
║ ⚪ #35 [question] Muse push fails… ║
║ ─ no anchors needs triage @aaronrene ║
╚═══════════════════════════════════════════════╝
Layout D — Sidebar Intelligence Panels
╔═══════════════════════════════════════╗
║ 🔥 HOTSPOT SYMBOLS ║
║ ──────────────────────────────────── ║
║ compute_snapshot_id 8 issues 12× ║
║ write_commit 4 issues 8× ║
║ _replay_branch 2 issues 3× ║
╠═══════════════════════════════════════╣
║ 💥 BLAST RISK ║
║ ──────────────────────────────────── ║
║ #42 compute_snapshot_id blast: 47 ║
║ #38 write_commit blast: 31 ║
║ #29 canonical_message blast: 18 ║
╠═══════════════════════════════════════╣
║ 🤖 AGENT ACTIVITY (last 7 days) ║
║ ──────────────────────────────────── ║
║ agentception-w03 5 issues ║
║ stori-bot 2 issues ║
╠═══════════════════════════════════════╣
║ 📦 RELEASED (may be fixed) ║
║ ──────────────────────────────────── ║
║ #29 v0.9.2 · #31 v0.9.1 ║
╚═══════════════════════════════════════╝
Layout E — Issue Row Inline Expansion (HTMX)
┌─────────────────────────────────────────────────────────────────────────────┐
│ 🔴 #42 [bug] [performance] ◈ compute_snapshot_id ▾ expanded │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SYMBOL INTEL (compute_snapshot_id) │
│ blast direct: 31 blast cross: 16 churn: 12 changes/30d │
│ co-changes: write_snapshot (8×), write_commit (6×) │
│ │
│ PROPOSAL COVERAGE: none — no open or merged proposals touch this symbol │
│ │
│ RELEASE CONTEXT: not yet in any release │
│ │
│ LINKED COMMITS: none │
│ │
│ AGENT PROVENANCE: agentception-w03 · claude-opus-4-6 · filed 2d ago │
│ │
│ [ view full issue ] [ open in CLI: muse hub issue read 42 ] │
└─────────────────────────────────────────────────────────────────────────────┘
The Enriched List Row Data Model
Each row needs more than IssueResponse currently exposes. New IssueListEntry:
class IssueListEntry(TypedDict):
"""Enriched row for the issues list view.
Extends IssueResponse with computed code intelligence fields.
All computed fields are produced by enrich_issue_list_entry() —
a single function that is the source of truth for what the list
shows per row, batched across the full page to avoid N+1 queries.
"""
# ── Core (from IssueResponse / MusehubIssue) ──────────────────────────
issue_id: str
number: int
title: str
state: str # "open" | "closed"
labels: list[str]
author: str
author_identity_type: str # "human" | "agent" | "org"
agent_id: str # "" if human
model_id: str # "" if human
assignee: str | None
assignee_identity_type: str # "human" | "agent" | "org"
created_at: str # ISO-8601
updated_at: str
comment_count: int
symbol_anchors: list[str] # ["file.py::Symbol", …]
commit_anchors: list[str] # ["abc123…", …]
# ── Symbol intelligence (from musehub_symbol_indexer) ─────────────────
primary_anchor: str | None # first/highest-blast symbol address
primary_anchor_short: str | None # short name for display (after "::")
max_blast_radius: int # max blast (direct + cross) across all anchors
max_churn_count: int # max change_count across all anchors
anchor_has_test_gap: bool # any anchor has zero test proxies
anchor_is_dead_code: bool # any anchor flagged as dead code
anchor_co_changes: list[str] # top 3 co-changing symbol short names
# ── Proposal coverage ─────────────────────────────────────────────────
linked_proposal_count: int # total linked proposals (any state)
open_proposal_count: int # open proposals covering this issue
merged_proposal_count: int # merged proposals (fix landed)
latest_proposal_number: int | None
latest_proposal_state: str | None # "open" | "merged" | "closed"
# ── Release context ───────────────────────────────────────────────────
released_in: list[str] # semver tags where fix landed (from commit anchors)
latest_release: str | None # most recent release tag
# ── Row health classification ─────────────────────────────────────────
health_class: str
# "hotspot" — open, blast > threshold OR churn > threshold
# "covered" — open, has linked open proposal
# "released" — open, linked merged proposal OR in a release
# "needs_triage" — open, zero symbol anchors
# "stale" — open, no activity in > 30 days
# "closed" — state == "closed"
enrich_issue_list_entry() — The Core Service Function
async def enrich_issue_list_entry(
issue: IssueResponse,
db: AsyncSession,
*,
intel_snapshot: SymbolIntelMap,
proposal_index: dict[str, list[_ProposalMatch]],
identity_index: dict[str, str],
release_index: dict[str, list[str]],
) -> IssueListEntry:
"""Compute all display-facing fields for a single issue list row.
All four index arguments are pre-fetched once per page request and
shared across all rows — this function does zero additional DB queries.
intel_snapshot: output of load_intel_snapshot() — symbol → blast/churn
proposal_index: issue_id → list[_ProposalMatch] — pre-resolved proposals
identity_index: handle → identity_type — "human"|"agent"|"org"
release_index: commit_id → list[semver_tag] — commits in which releases
health_class is derived in order:
1. "closed" if state == "closed"
2. "hotspot" if max_blast_radius > BLAST_HOTSPOT_THRESHOLD (default 20)
OR max_churn_count > CHURN_HOTSPOT_THRESHOLD (default 10)
3. "released" if any commit_anchor is in release_index
OR merged_proposal_count > 0
4. "covered" if open_proposal_count > 0
5. "needs_triage" if len(symbol_anchors) == 0
6. "stale" if updated_at < now - 30 days
7. "open" (default)
Performance contract: O(anchors) dict lookups only. Typical latency
for a 25-row page: <10ms (pure in-memory after pre-fetch).
Args:
issue: IssueResponse for this row.
db: Async session (not used — all data comes from indexes).
intel_snapshot: Pre-loaded symbol intelligence map.
proposal_index: Pre-resolved proposal coverage per issue_id.
identity_index: Pre-loaded handle → identity_type mapping.
release_index: Pre-loaded commit → release tag mapping.
Returns:
IssueListEntry with all fields populated.
Raises:
ValueError: If health_class cannot be determined (should never occur).
"""
New Filter Dimensions
class IssueListFilters(BaseModel):
"""Extended query parameters for the issues list page.
All existing filters (state, label, assignee, author, sort) are preserved.
New filters target Muse-specific code intelligence signals.
"""
state: Literal["open", "closed"] = "open"
label: str | None = None
assignee: str | None = None
author: str | None = None
author_type: Literal["human", "agent", "org", "all"] = "all"
symbol: str | None = None # filter by anchor symbol address (substring match)
health_class: str | None = None # hotspot | covered | needs_triage | stale | released
has_proposal: bool | None = None # True = has linked proposal, False = no proposal
blast_min: int | None = None # minimum blast radius on any anchor
sort: Literal[
"newest", "oldest", "most-commented",
"blast_desc", # highest blast radius first
"churn_desc", # highest churn count first
"triage_first", # needs_triage issues first
"stale_first", # least recently updated first
] = "newest"
cursor: str | None = None
limit: int = Field(default=25, ge=1, le=100)
HTMX Architecture
Three layers, same as the proposals list:
Layer 1: SSR full page
GET /{owner}/{repo}/issues?state=open
→ renders health pulse bar, symbol heat header, sidebar panels, row fragment
Layer 2: HTMX tab/filter swap
hx-get="/{owner}/{repo}/issues/rows"
hx-target="#issue-rows"
hx-push-url="true"
→ bare fragment; replaces only the row list, not the chrome
Layer 3: HTMX row inline expansion
hx-get="/{owner}/{repo}/issues/{number}/summary"
hx-target="#issue-row-{number}-detail"
hx-swap="innerHTML"
→ symbol intel, proposal coverage, release context, agent provenance
Fragment endpoints
GET /{owner}/{repo}/issues/rows ← #issue-rows fragment
GET /{owner}/{repo}/issues/heat ← #symbol-heat fragment (symbol heat bar only)
GET /{owner}/{repo}/issues/{n}/summary ← inline row expansion
New / Extended API Endpoints
GET /api/repos/{repo_id}/issues
NEW params: author_type, symbol, health_class, has_proposal, blast_min,
sort (blast_desc | churn_desc | triage_first | stale_first)
GET /api/repos/{repo_id}/issues/heat
Returns: per-symbol aggregation across all open issues
{
"symbols": [
{"address": "file.py::Sym", "short": "Sym", "issue_count": 8,
"churn_count": 12, "blast_direct": 31, "blast_cross": 16}
],
"unanchored_count": 3,
"total_open": 14
}
GET /api/repos/{repo_id}/issues/triage
Returns: issues with zero symbol anchors (health_class="needs_triage")
Purpose: dedicated triage queue for humans and agents
Symbol Anchors & Blast Radius
Existing symbols (current list page)
| Symbol | File | Notes |
|---|---|---|
issue_list_page |
musehub/api/routes/musehub/ui_issues.py |
Main SSR route — extend with new filters |
list_issues |
musehub/services/musehub_issues.py |
DB query — add symbol, health_class, blast_min, sort params |
find_proposals_by_commit_graph |
musehub/services/musehub_issues.py |
Proposal resolution signal 1 |
find_proposals_by_branch_reachability |
musehub/services/musehub_issues.py |
Proposal resolution signal 2 |
find_proposals_by_symbol_overlap |
musehub/services/musehub_issues.py |
Proposal resolution signal 3 |
get_issue_release_context |
musehub/services/musehub_issues.py |
Release context for detail page |
load_intel_snapshot |
musehub/services/musehub_symbol_indexer.py |
Repo-wide code intelligence |
lookup_symbol_intel |
musehub/services/musehub_symbol_indexer.py |
Per-address intel lookup |
get_timeline |
musehub/services/musehub_issues.py |
Event + comment timeline |
MusehubIssue |
musehub/db/musehub_models.py |
ORM model |
MusehubIssueEvent |
musehub/db/musehub_models.py |
Event timeline ORM |
New symbols to create
| Symbol | File | Purpose |
|---|---|---|
IssueListEntry |
musehub/models/musehub.py |
Enriched row TypedDict |
IssueListFilters |
musehub/models/musehub.py |
Extended query params |
SymbolHeatEntry |
musehub/models/musehub.py |
Per-symbol aggregation for heat bar |
SymbolHeatResponse |
musehub/models/musehub.py |
/issues/heat response |
enrich_issue_list_entry |
musehub/services/musehub_issues.py |
Per-row enrichment (zero DB) |
enrich_issue_list_batch |
musehub/services/musehub_issues.py |
Parallel batch enrichment |
prefetch_issue_list_indexes |
musehub/services/musehub_issues.py |
Pre-loads all 4 index maps |
get_symbol_heat |
musehub/services/musehub_issues.py |
Symbol heat aggregation |
classify_issue_health |
musehub/services/musehub_issues.py |
health_class derivation |
issue_rows_fragment |
musehub/api/routes/musehub/ui_issues.py |
HTMX row fragment route |
issue_row_summary |
musehub/api/routes/musehub/ui_issues.py |
Inline row expansion |
issue_heat_fragment |
musehub/api/routes/musehub/ui_issues.py |
Symbol heat HTMX swap |
issue_triage_page |
musehub/api/routes/musehub/ui_issues.py |
Dedicated triage queue page |
initIssueList |
src/ts/pages/issue-list.ts |
Extended TS controller |
Blast radius
musehub/models/musehub.py
IssueListEntry, IssueListFilters, SymbolHeatEntry, SymbolHeatResponse
↓
musehub/services/musehub_issues.py
enrich_issue_list_entry, enrich_issue_list_batch, prefetch_issue_list_indexes,
get_symbol_heat, classify_issue_health, list_issues (extended)
↓
musehub/api/routes/musehub/ui_issues.py
issue_list_page (extended), issue_rows_fragment (new),
issue_row_summary (new), issue_heat_fragment (new), issue_triage_page (new)
musehub/api/routes/musehub/issues.py
list_issues JSON endpoint (new filter params: symbol, health_class, blast_min, sort)
+ GET /issues/heat, GET /issues/triage
↓
musehub/templates/musehub/pages/issue_list.html (extend)
musehub/templates/musehub/fragments/issue_rows.html (extend)
musehub/templates/musehub/fragments/issue_row_detail.html (new)
musehub/templates/musehub/fragments/symbol_heat.html (new)
musehub/templates/musehub/pages/issue_triage.html (new)
src/ts/pages/issue-list.ts (extend)
8-Tier Test Plan
Tier 1 — Shape / Schema
IssueListEntryTypedDict has all keys includinghealth_class,max_blast_radius,primary_anchor,linked_proposal_count,released_inIssueListFiltershas correct defaults:state="open",sort="newest",limit=25,author_type="all"GET /api/repos/{id}/issues/heatreturnssymbolslist andunanchored_countenrich_issue_list_entryis importable frommusehub.services.musehub_issuesclassify_issue_healthreturns one of the 7 defined stringsIssueListEntry.author_identity_typeis one of"human","agent","org"SymbolHeatEntryhasaddress,short,issue_count,churn_count,blast_direct,blast_cross
Tier 2 — Round-Trip / Integration
- Create 3 issues: one anchored to high-blast symbol, one with proposal, one with no anchors → list shows correct
health_classfor each sort=blast_desc→ issue withmax_blast_radius=47appears before issue withmax_blast_radius=12sort=triage_first→ unanchored issue appears first regardless of creation datehealth_class=hotspotfilter → only issues with blast > threshold returnedhealth_class=needs_triagefilter → only unanchored issues returnedsymbol=compute_snapshot_idfilter → only issues anchoring that symbol returnedhas_proposal=Truefilter → only issues with at least one linked proposal returnedauthor_type=agentfilter → only issues withagent_id != ""returned- Create issue → assign proposal →
linked_proposal_countincrements in list row - Merge proposal →
merged_proposal_countincrements,health_classmay becomereleased
Tier 3 — Edge Cases
- Repo with zero issues → list returns
[], health pulse shows0 OPEN - Issue with 50 symbol anchors →
primary_anchor= highest-blast one, no crash - Issue with commit anchor matching no release →
released_in = [] - Issue with commit anchor matching 3 releases →
released_inhas all 3 - Symbol from
intel_snapshotmissing for an anchor → graceful fallback,max_blast_radius=0 blast_min=0→ all issues returned (not filtered out)blast_min=9999→ empty list, not error- Issue where
agent_id=""but author matches an agent handle in DB → correctly classified as agent sort=stale_firstwith all issues updated today → stable sort by created_at
Tier 4 — Stress
- 1,000 open issues →
list_issueswithlimit=25returns in under 50ms prefetch_issue_list_indexesfor 1,000 issues → under 100ms (batch queries)enrich_issue_list_batchfor 25 issues → under 20ms (pure in-memory after pre-fetch)get_symbol_heatwith 500 open issues across 50 symbols → under 30ms- 100 concurrent requests to
/issues/rows→ no deadlock, all under 200ms sort=blast_descon 1,000 issues → in-memory sort under 5ms
Tier 5 — Data Integrity
primary_anchoris always the anchor with the highestmax_blast_radiusacross all anchorsmax_blast_radius=max(blast_direct + blast_cross)across allsymbol_anchorsclassify_issue_healthreturns"hotspot"whenmax_blast_radius > BLAST_HOTSPOT_THRESHOLDlinked_proposal_count=len(proposal_index[issue_id])exactlyreleased_in= union ofrelease_index[commit_id]for allcommit_anchorsagent_filed_counton stats bar =len([i for i in all_open if i.agent_id])- Symbol heat
issue_countfor address X =len([i for i in open_issues if X in i.symbol_anchors]) prefetch_issue_list_indexes→identity_indexcovers everyauthorandassigneehandle in the page
Tier 6 — Performance / Benchmarks
enrich_issue_list_entryfor single issue → under 0.5ms (pure dict lookups)- Full
issue_list_pageSSR for 25 rows → under 150ms end-to-end issue_rows_fragment→ under 60ms (rows only, no chrome)issue_row_summaryinline expansion → under 15msissue_heat_fragment→ under 20ms (single aggregation + sort)- All
list_issuesqueries verified to useix_musehub_issues_repo_stateindex
Tier 7 — Security
symbolfilter param with path traversal (../../etc/passwd) → rejected or matched against allowlist, never passed to SQL rawhealth_classfilter with unmapped value → 422 from Pydantic, not 500blast_min=-1→ 422 (ge=0 validation)authorfilter with control chars / null bytes → sanitized via_validate_assigneepattern before any querylimit=10000→ clamped to 100, not a full-table scan- Private repo issues → anonymous GET returns 403, not issue data
- Agent-filed issue with forged
agent_id(not matching any registered agent) → shown withauthor_identity_type="agent"(no crash, no info leak) cursorwith tampered ISO-8601 string → graceful fallback to start, no 500
Tier 8 — Docstrings / API Contract
enrich_issue_list_entryhas full Args, Returns, Raises, and performance contract (zero DB queries)classify_issue_healthdocuments eachhealth_classvalue and the priority orderprefetch_issue_list_indexesdocuments all four index structures and their key typesget_symbol_heatdocuments the aggregation formula and sort orderIssueListEntryhas field-level descriptions for every non-obvious fieldIssueListFiltersdocumentssort=blast_desc,sort=triage_first,sort=stale_firstissue_row_summarydocuments HTMX swap target and what data it exposesBLAST_HOTSPOT_THRESHOLDandCHURN_HOTSPOT_THRESHOLDare module-level constants with comments explaining the calibration
7-Phase Implementation Plan
Phase 1 — Data Models & Enrichment Core
Branch: task/issue-list-models
- Define
IssueListEntryTypedDict inmusehub/models/musehub.py - Define
IssueListFiltersPydantic model with all new filter params - Define
SymbolHeatEntry,SymbolHeatResponse - Write
prefetch_issue_list_indexes(db, repo_id, issues)— batches all four pre-fetch queries (symbol intel, proposal coverage, identity types, release context) - Write
classify_issue_health(entry_partial) -> str— pure function, no DB - Write
enrich_issue_list_entry(issue, *, intel_snapshot, proposal_index, identity_index, release_index) -> IssueListEntry— zero additional DB queries - Write
enrich_issue_list_batch(issues, db) -> list[IssueListEntry]— callsprefetch_issue_list_indexesthenenrich_issue_list_entryper row - Tier 1 + 5 tests
Symbols: IssueListEntry, IssueListFilters, enrich_issue_list_entry, enrich_issue_list_batch, prefetch_issue_list_indexes, classify_issue_health
Phase 2 — Symbol Heat & Extended Sort
Branch: task/issue-list-heat
get_symbol_heat(db, repo_id, state) -> SymbolHeatResponse— single aggregation query; joinsmusehub_issues.symbol_anchorswithper_symbol_intel_json- Extend
list_issues(...)withsymbol,health_class,has_proposal,blast_minfilter params and new sort values GET /api/repos/{id}/issues/heatendpointGET /api/repos/{id}/issues/triageendpoint- Tier 2 + 6 (round-trip + performance) tests
Symbols: get_symbol_heat
Phase 3 — UI Route Extensions
Branch: task/issue-list-routes
- Extend
issue_list_pageSSR route: callenrich_issue_list_batch, passIssueListEntrylist +SymbolHeatResponseto template context - Add
issue_rows_fragmentroute — bare#issue-rowsswap, no chrome - Add
issue_row_summaryroute — single-issue inline expansion with full symbol intel panel - Add
issue_heat_fragmentroute —#symbol-heatHTMX swap target - Add
issue_triage_pageroute — dedicated triage queue (zero-anchor open issues) - Tier 3 + 7 tests (edge cases + security)
Symbols: issue_rows_fragment, issue_row_summary, issue_heat_fragment, issue_triage_page
Phase 4 — Template Rewrite
Branch: task/issue-list-templates
- Extend
musehub/templates/musehub/pages/issue_list.html:- Health pulse bar (health_class counts + agent count)
- Symbol heat header (top 5 symbols by issue_count)
- Extended filter bar: author_type, health_class, symbol, has_proposal dropdowns
- Sidebar: hotspot panel, blast risk panel, agent activity, released-may-be-fixed
- Extend
musehub/templates/musehub/fragments/issue_rows.html:- Health class state indicator (colour dot)
- Primary anchor badge with blast + churn inline
- Proposal coverage indicator (✅ / ✗)
- Agent badge with model name
- Release badge (📦 v0.9.2)
- Create
musehub/templates/musehub/fragments/issue_row_detail.html:- Symbol intel panel, proposal coverage list, release context, agent provenance, CLI command hint
- Create
musehub/templates/musehub/fragments/symbol_heat.html - Create
musehub/templates/musehub/pages/issue_triage.html
Phase 5 — TypeScript Controller
Branch: task/issue-list-ts
Extend src/ts/pages/issue-list.ts:
- Handle HTMX
after-swapon#issue-rows— re-sync URL params, restart stagger animations - Handle
#symbol-heatswap — animate bar width transitions - Inline row expansion: toggle
aria-expanded, animate detail panel height - Health class colour system: CSS custom properties for each class colour
- Persist filter selections in URL params with
history.replaceState
Phase 6 — MCP Context Extension
Branch: task/issue-list-mcp
- Extend
ActiveIssueContextinmusehub/models/musehub_context.pywith list-relevant fields:health_class,max_blast_radius,max_churn_count,linked_proposal_count,released_in - Add
list_issues_context(repo_id, filters)MCP tool — returnsIssueListEntrylist for agent consumption - Agents can now scan the issue queue as machine-readable data and act (anchor, triage, file proposals) on specific issues
Phase 7 — Index Optimisation
Branch: task/issue-list-indexes
- Add composite index
(repo_id, state, updated_at)forsort=stale_first - Add GIN index on
symbol_anchorsJSON column forsymbol=filter (PostgreSQL) - Verify all list queries hit correct indexes via
EXPLAIN ANALYZEharness in tests - Add
BLAST_HOTSPOT_THRESHOLD = 20andCHURN_HOTSPOT_THRESHOLD = 10as tunable constants inmusehub/config.py
What Makes This Uniquely Muse
GitHub's issue list: title, label badge, author avatar, date, comment count. Zero code intelligence.
This list:
Symbol heat at the page level — before you open a single issue, you can see which symbols are generating the most friction across the entire open issue queue.
compute_snapshot_idhas 8 open issues? That's a systemic problem with that function, not 8 isolated bugs.Blast radius per row — every issue anchored to a symbol with blast > 20 is visually flagged. Picking which issue to fix first is now informed by actual architectural risk, not gut feeling.
needs_triageas a first-class state — unanchored issues are explicitly surfaced. An unanchored issue is not just missing metadata — it's an issue the codebase can't reason about. The triage queue makes that visible and actionable.Agent provenance is structural — when an agent files an issue, the
agent_idandmodel_idare in the database, cryptographically tied to the commit that triggered the issue. The list shows@agentception-w03 [claude-opus-4-6]with the same confidence as@gabriel [human].Proposal coverage per row — if someone is already working on a fix (open proposal that touches this symbol), the list shows it. No duplicate effort.
Release context at a glance — the list can show whether an issue's commit anchors have already landed in a release. This is the difference between "open but already fixed" and "open and definitely broken."
Assignee
@aaronrene — this is the companion to issues #2 (proposals list) and the issue detail reimagination doc already in docs/roadmaps/issues-reimagined.md. The detail page is strong; the list page is where a developer or agent spends most of their time deciding what to work on next. That decision surface deserves the full intelligence stack.
Priority: High — the list page is the entry point to the issues system. Every other issue feature is invisible until the list makes it legible.