muse symlog — Follow-up Items
Background
Issue #51 delivered the full symlog foundation: per-symbol journaling at commit time,
muse symlog read CLI with filters, lifecycle management (expire/delete/GC), and
@{N} ref resolution for muse code cat, muse symlog resolve, and
muse symlog diff. All 55 test IDs (SL_01–SL_55) are green.
This issue captures the items explicitly deferred from #51 plus useful extensions that emerged during implementation.
Deferred from #51
1. Analytics commands — muse symlog coupling and muse symlog hotspots ✅ IMPLEMENTED
The live symlog journal is a rich, already-written dataset. Two analytics commands can read it in O(1) per symbol without touching commit history:
muse symlog hotspots [--top N] [--file path] [--json]
— ranks symbols by churn (number of symbol-modified entries in the journal).
Complements muse code hotspots (which reads commit history) with a zero-scan
alternative that is always up to date.
muse symlog coupling [--file path] [--json]
— finds symbol pairs that share commit IDs across their logs (i.e. they always
change together). Surfaced purely from symlog data, no AST traversal needed.
2. muse blame integration ✅ IMPLEMENTED
muse blame "file.py::Symbol" currently reconstructs authorship by scanning
commit history. With symlogs in place, the per-symbol change record already
contains author and commit ID at each index. blame should prefer the symlog
when available — O(1) per symbol instead of O(commits).
Note on command path: the actual command is muse code blame ADDRESS
(same naming situation as item 7). It now prefers the symlog journal when
one exists, falling back to the full BFS commit-history scan only when: no
journal exists yet, --from REF is given (the journal has no concept of
"as reachable from an older ref" the way the BFS walk's starting boundary
does), or --kind moved/--kind signature is requested (categories the
journal's created/modified/deleted/renamed classification can never
produce). Rename chains are followed via the journal's own born_from
pointers, matching the pre-existing rename-tracking behavior.
3. muse code narrative rewrite
muse code narrative "file.py::Symbol" generates a plain-English description of
a symbol's lifecycle. It currently works from AST diffs across commits. The symlog
journal is the canonical source of that history now — narrative should read from
symlog entries, gaining richer metadata (operation, author, born-from chains).
4. Remote symlog sharing
Symlogs are currently local-only — they are not pushed or pulled. For teams sharing a MuseHub remote, symlogs are silently missing on clones. The correct design:
muse pushbundles.muse/symlogs/alongside commits.muse pullmerges incoming symlog entries (append-only, no conflicts).muse bundle createincludes symlogs in the msgpack payload.
5. Symlog entries for non-code domains — ⏸️ CONFIRMED STILL BLOCKED
The extractor returns {} for non-parseable files, so MIDI tracks, binary assets,
and schema files produce no symlog entries. For music repos this is the wrong
trade-off: a MIDI symbol (track::chorus) should produce a symlog entry when
the Harmony domain plugin identifies a change. Deferred until the domain plugin
API supports content-ID extraction for non-code artifacts.
Re-verified while clearing this backlog (not just left alone on faith):
extract_symbols()/_extract_body_hashes() in muse/core/symlog.py import
muse.plugins.code.ast_parser.parse_symbols directly and unconditionally —
there is no MuseDomainPlugin.extract_symbols()-style dispatch hook a MIDI
(or any other non-code) plugin could implement instead. The existing MIDI
plugin (muse/plugins/midi/plugin.py) does have note-level content-ID
concepts, but only for its own 3-way merge logic — nothing symlog could call
into today. Building the generic domain-plugin symbol-extraction interface
(and the MIDI-side implementation of it) is a substantial feature in its own
right, not a quick add alongside this ticket's other items — genuinely still
blocked, correctly left open rather than hacked around.
6. Relative time syntax (@{1.day.ago}, @{yesterday}) ✅ IMPLEMENTED
@{N} addresses work by index. Git's reflog also supports relative time syntax
(HEAD@{1.hour.ago}). Symlog --since/--until cover the filtering use case,
but @{1.day.ago} as a direct ref specifier is more ergonomic in agent prompts.
New items from implementation
7. muse checkout-symbol "file.py::Symbol@{N}" ✅ IMPLEMENTED
Restore a prior version of a single symbol to disk without touching any other
file. The natural extension of muse code cat "addr@{N}" — cat reads, checkout
restores. Critically useful for surgical recovery: "undo the change to this one
function without reverting the whole commit."
Note on command path: this command already existed as muse code checkout-symbol ADDRESS --commit REF (not a bare top-level muse checkout-symbol, despite this ticket's heading) — it restores a symbol
surgically into the working tree, splicing only that symbol's lines. The
missing piece this item actually delivers: ADDRESS can now embed a symlog
ref directly ("file.py::Symbol@{N}"), resolved via the existing
resolve_symlog_addr(), making --commit optional. No new top-level alias
was added — one name for one command, per workspace convention.
8. muse symlog grep PATTERN [--file path] [--json] ✅ IMPLEMENTED
Search symbol bodies across their full symlog history. Answers: "when did this
symbol last contain the string deprecated?" or "which version of this function
called session.add?" Reads object store bodies via resolve_symbol_body for
each entry.
9. muse symlog show "file.py::Symbol@{N}" ✅ IMPLEMENTED
Convenience alias for muse code cat "file.py::Symbol@{N}" with human-focused
output: operation, author, timestamp header, then the full symbol body. Useful for
interactive exploration ("show me what this function looked like two changes ago").
10. muse symlog stat [--file path] [--json] ✅ IMPLEMENTED
Summary statistics for a symbol or file: total entries, operation breakdown (created/modified/deleted/renamed), most active author, first and last commit IDs, date range. Useful for code review context and agent decision-making.
11. Symlog entries for muse revert ✅ IMPLEMENTED
muse revert records a reflog entry for the branch ref move, but writes no symlog
entries for the symbols it touches. A revert of a commit that modified compute_total
should produce a symbol-modified: revert <commit_id> entry in that symbol's log.
12. muse symlog prune-orphans [--dry-run] [--json] ✅ IMPLEMENTED
Find symlog files whose symbol address no longer exists in the current snapshot
and remove them. Analogous to muse gc for unreachable objects — keeps the
.muse/symlogs/ tree clean after large-scale renames or file deletions.