gabriel / musehub public
Closed #12 Enhancement
filed by gabriel human · 47 days ago

/intel/stable — stability leaderboard

0 Anchors
Blast radius
Churn 30d
0 Proposals

Overview

Stability leaderboard — symbols that have not changed in the longest time. The antithesis of blast-risk: instead of flagging what is dangerous to touch, this surface celebrates what is safe to depend on.

╔═══════════════════════════════════════════════════════════════════╗
║                                                                   ║
║   ❄  STABLE SYMBOLS                                              ║
║                                                                   ║
║   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        ║
║   │  1,204   │  │    87    │  │  2.4 yr  │  │   312    │        ║
║   │  TOTAL   │  │ ETERNAL  │  │  OLDEST  │  │ 90+ DAYS │        ║
║   └──────────┘  └──────────┘  └──────────┘  └──────────┘        ║
║                                                                   ║
║   ❄  utils/hash.py::sha256_hex          ········ 912 days        ║
║   ❄  core/types.py::split_id            ········ 844 days        ║
║   ❄  auth/keys.py::derive_keypair       ········ 731 days        ║
║   ❄  models/repo.py::RepoId             ········ 688 days        ║
║   ❄  codec/msgpack.py::pack_frame       ········ 601 days        ║
║                                                                   ║
╚═══════════════════════════════════════════════════════════════════╝

Data shape

musehub_intel_stable {
  repo_id            VARCHAR(128)   PK / FK → musehub_repos
  address            VARCHAR(512)   PK
  days_stable        INTEGER        days since last modification
  since_start        BOOLEAN        never modified in full history
  last_changed_commit VARCHAR(128)  commit_id of last modification (nullable)
  ref                VARCHAR(128)   head commit when last computed
}

last_changed_commit is new — added in Phase 1 migration.


Provider logic (pure SQL — no subprocess)

Source table: musehub_symbol_intel

days_stable        = (NOW() - last_changed).days   when last_changed IS NOT NULL
since_start        = (churn = 0)
last_changed_commit = last_commit_id               from musehub_symbol_intel
candidate filter   = churn_30d = 0 AND churn_90d = 0 AND last_changed IS NOT NULL

Stability tiers (display only, not stored):

Tier Days Color Label
eternal since_start --color-teal
veteran 365+ --color-teal 1yr+
stable 180–364 --color-success 6mo+
settling 90–179 --color-accent 90d+
recent 30–89 --text-secondary 30d+

Theme elements

From the Spectral Theme catalog (docs/guides/spectral-theme.md):

  • Icon: snowflake — the frozen/crystalline motif
  • Primary color: --color-teal (#2dd4bf) — cold, stable, trustworthy
  • Secondary: --color-success (#3fb950) — for veteran tier
  • CSS prefix: .ss-* (stable symbols)
  • Stat card pattern: same flex column as .br-stat-card
  • Row pattern: same flex row as .br-row — clickable anchor tag

SCSS files:

  • src/scss/components/_stable.scss — visual rules
  • src/scss/pages/_stable.scss — structural layout

Phases (load-bearing order)

Phase 1 — Migration + Pure SQL Provider (foundation)

Goal: musehub_intel_stable has last_changed_commit; StableProvider derives data entirely from musehub_symbol_intel without a subprocess.

Deliverables:

  1. alembic/versions/0008_stable_last_changed_commit.py — ADD COLUMN migration
  2. MusehubIntelStable model updated with last_changed_commit mapped column
  3. StableProvider.compute() rewritten — pure SQL, no _run_muse subprocess
  4. Strong docstrings on all new/modified symbols
  5. Seven-tier test suite in tests/test_phase1_stable_provider.py

Test tiers (target ≥ 30 tests):

  • Unit (P1_01–P1_06): _days_stable_from_dt() helper — epoch, now, past, future clamp, None input, timezone-naive
  • Integration (P1_07–P1_14): Provider seeded with symbol_intel rows; verify upsert, days_stable value, since_start flag, last_changed_commit populated, zero-churn filter, non-zero-churn excluded, rerun updates in place, ref column updated
  • E2E (P1_15–P1_18): Full push → worker → DB round-trip verifying row count > 0, days_stable > 0, last_changed_commit is a sha256: prefixed string, since_start only set when churn = 0
  • Stress (P1_19–P1_21): 500 symbol rows; verify all upserted, no duplicates, upsert is idempotent
  • Data Integrity (P1_22–P1_24): NULL last_changed excluded, symbol_kind filter respected, address uniqueness per repo
  • Performance (P1_25–P1_26): 1000-row batch completes < 5s, second run (all conflicts) completes < 5s
  • Security (P1_27–P1_28): SQL injection attempt in address field stored verbatim (no execution), repo isolation (repo A rows not visible to repo B query)

Phase 2 — Route + List Page

Goal: GET /{owner}/{repo_slug}/intel/stable renders the leaderboard.

Deliverables:

  1. Route intel_stable_page in ui_intel.py
  2. Template musehub/pages/intel_stable.html
  3. Context vars: symbols, total_count, eternal_count, veteran_count, max_days_stable, selected_top, valid_tops
  4. Filter bar: ?since_start=true toggle + ?top=N selector (25/50/100)
  5. Seven-tier test suite in tests/test_phase2_stable_route.py

Test tiers (target ≥ 20 tests):

  • Unit (P2_01): Route registered in router.routes
  • Integration (P2_02–P2_08): Empty state 200, populated 200, since_start filter, top=N limit, stat counts correct, address appears in HTML, days_stable value in HTML
  • E2E (P2_09–P2_11): Full seed → HTML round-trip; stat row rendered; filter URL preserves other params
  • Stress (P2_12–P2_13): 200 rows render without error; ?top=100 returns correct count
  • Data Integrity (P2_14–P2_15): Only rows for this repo returned; deleted repo rows not shown
  • Performance (P2_16–P2_17): Response < 500ms for 100 rows; sort order is days_stable DESC
  • Security (P2_18–P2_20): XSS in address escaped; path traversal in owner/slug → 404; IDOR — repo B rows not shown on repo A page

Phase 3 — SCSS

Goal: Visual identity for the stable surface.

Deliverables:

  1. src/scss/components/_stable.scss
    • .ss-stat-card with --teal / --success / --accent border variants
    • .ss-row as <a> — hover, link reset, teal left-accent on hover
    • .ss-days — large mono number, teal for veteran/eternal
    • .ss-tier-badge — per-tier color mapping
    • .ss-address — file + :: + name in mono
    • .ss-meta — last-changed commit chip
    • .icon-snowflake color rule → --color-teal
  2. src/scss/pages/_stable.scss — structural flex layout
  3. @use wired into app.scss
  4. Built app.css committed

Phase 4 — Detail Page

Goal: GET /{owner}/{repo_slug}/intel/stable/detail?address= — per-symbol view.

Deliverables:

  1. Route intel_stable_detail_page in ui_intel.py
  2. Template musehub/pages/intel_stable_detail.html
  3. Shows: days_stable bar (normalized to max in repo), since_start badge, last_changed_commit link, address/kind header
  4. Empty-state for unknown address (200 not 404)
  5. Seven-tier test suite in tests/test_phase4_stable_detail.py (≥ 22 tests, mirrors blast-risk detail structure)

Phase 5 — Navigation Integration

Goal: Intel Hub card + breadcrumb wired up.

Deliverables:

  1. intel_dashboard.html — add stable card and alert row (❄ N eternally stable symbols)
  2. Breadcrumb chain: intel → stable → detail
  3. intel_stable.html back link to /intel
  4. intel_stable_detail.html back link to /intel/stable
  5. Five-tier test suite in tests/test_phase5_stable_nav.py

Acceptance criteria

  • musehub_intel_stable.last_changed_commit column exists in production schema
  • StableProvider contains zero subprocess calls
  • /gabriel/musehub/intel/stable renders with data on staging
  • All rows are clickable → detail page
  • Snowflake icon is teal
  • Stat cards show total, eternal, veteran, max age
  • All 7 test tiers pass in CI
Activity5
gabriel opened this issue 47 days ago
gabriel 47 days ago

Phase 1 complete ✅

28/28 tests passing across all 7 tiers.

Delivered

  • alembic/versions/0008_stable_last_changed_commit.py — ADD COLUMN migration for last_changed_commit VARCHAR(128)
  • MusehubIntelStable ORM model updated with new column + full docstring
  • StableProvider rewritten — zero subprocess calls; pure SQL from musehub_symbol_intel using churn_30d = 0 AND churn_90d = 0 AND last_changed IS NOT NULL
  • _days_stable_from_dt() pure helper with docstring and 6 unit tests
  • tests/test_phase1_stable_provider.py — 28 tests, all green

Test breakdown

Tier Tests Status
Unit P1_01–P1_06
Integration P1_07–P1_14
E2E P1_15–P1_18
Stress P1_19–P1_21
Data Integrity P1_22–P1_24
Performance P1_25–P1_26
Security P1_27–P1_28

Merged to dev, pushed to local. Starting Phase 2.

gabriel 47 days ago

Phase 2 complete ✅

20/20 tests passing across all 7 tiers.

Delivered

  • intel_stable_page route — GET /{owner}/{repo_slug}/intel/stable
  • _stable_tier() helper — maps days + since_start to eternal/veteran/stable/settling/recent
  • intel_stable.html template — stat row, filter bar (?since_start, ?top), clickable symbol rows
  • tests/test_phase2_stable_route.py — 20 tests, all green

Test breakdown

Tier Tests Status
Unit P2_01
Integration P2_02–P2_08
E2E P2_09–P2_11
Stress P2_12–P2_13
Data Integrity P2_14–P2_15
Performance P2_16–P2_17
Security P2_18–P2_20

Merged to dev, pushed to local. Starting Phase 3.

gabriel 47 days ago

Design clarification — full CLI fidelity (applies to all intel pages)

The GUI must be a complete mirror of what muse code stable outputs from the command line. Every field, every filter, every meta value.

What the CLI outputs

Meta context (shown per-run):

from_ref:          "(beginning)" or previous head commit
to_ref:            current head commit
commits_analysed:  integer
truncated:         bool
filters:           {top, kind, language, since, include_imports, include_docs, max_commits}

Per-symbol:

address:               "file.py::symbol_name"
days_stable:           integer
since_start_of_range:  bool

What the GUI is missing (fixed before Phase 3)

  1. symbol_kind not stored in musehub_intel_stable — adding migration 0009
  2. StableProvider not storing meta (commits_analysed, from_ref, truncated) in musehub_intel_results
  3. Route missing ?kind= filter
  4. Template missing meta context panel and kind filter bar

Fix plan (inserted before Phase 3)

  • Migration 0009_stable_symbol_kind.py — ADD COLUMN symbol_kind VARCHAR(64)
  • StableProvider stores kind + enriches intel_results JSON with {count, commits_analysed, from_ref, to_ref, truncated}
  • Route loads meta from musehub_intel_results + adds ?kind= filter
  • Template gains meta context panel + kind filter chips

This philosophy applies to every intel page. Each one should show whatever muse code <cmd> shows — no less.

gabriel 47 days ago

Implemented in full:

  • BFS commit-walk algorithm mirrors muse code stable CLI exactly — days_stable is commit count from HEAD, not calendar days
  • Stable symbols leaderboard page at /intel/stable with tier badges (eternal/veteran/stable/settling/recent), kind filter chips, and paginated symbol list
  • Detail page at /intel/stable/detail?address=… showing days stable, last changed, lifetime churn, authors, blast radius, and provenance
  • Dashboard card (4th panel on /intel) showing total stable count, eternal count, and top-5 preview
  • All numbers formatted consistently across list and detail pages
gabriel 29 days ago

Phase 6 complete — caches and indices migrated to plain JSON

Commit: sha256:0d0f1e8e12ca

Changes

All cache files under .muse/cache/ and index files under .muse/indices/ now use plain JSON with .json extensions.

Source files updated (8):

  • muse/core/cache_base.py — replaced msgpack with json; binary-format guard (raw[0] > 0x7F → cache miss, no crash)
  • muse/core/stat_cache.py_CACHE_VERSION 3→4, stat.msgpackstat.json
  • muse/core/callgraph_cache.py_CACHE_VERSION 1→2, callgraph.msgpackcallgraph.json
  • muse/core/symbol_cache.py_CACHE_VERSION 1→2, symbols.msgpacksymbols.json
  • muse/core/implicit_edge_cache.py_CACHE_VERSION 1→2, implicit_edges.msgpackimplicit_edges.json
  • muse/plugins/code/_invariants.py_FILE_CACHE_VERSION 1→2, invariants.msgpackinvariants.json
  • muse/core/test_history.py_HISTORY_VERSION 1→2, test_history.msgpacktest_history.json
  • muse/core/indices.py_index_path uses .json; _read_msgpack_read_json; all serialisation uses json not msgpack
  • muse/core/paths.py — all cache/index path helpers updated to return .json filenames

Tests:

  • Added tests/test_phase6_caches_indices_json.py — 20 new tests covering JSON roundtrips, file extension assertions, and stale-msgpack-is-miss behaviour for all caches and indices
  • Updated 4 existing test files (test_callgraph_cache.py, test_core_stat_cache.py, test_core_symbol_cache.py, test_indices.py) to reflect new .json extensions and bumped version numbers

Phase 7 next

Remove import msgpack from all remaining non-wire files and enforce via lint test.