feat(muse): index Muse Code sessions as a first-class source - #259
barryollama wants to merge 12 commits into
Conversation
Add `muse` as a source so Meta's Muse Code CLI sessions reach discovery, sync, targeted hydration and live capture like every other harness. Muse writes one append-only, event-sourced `session.jsonl` per session under `$XDG_DATA_HOME/muse/sessions/YYYY/MM/DD/<id>/`. Every record carries its own `recorded_at` (microseconds), so no timestamp is inferred. The parser (`ingest/muse.rs`) reads prompts, prose, readable thinking, tool calls and results, file edits, per-model-step usage, models and lifecycle markers, filters records to the session's own stream (mirrored subagent/reminder task streams are not prompts), and takes tool status from `tool_batch.effect.terminal` plus a non-zero `bash` exit code. `subagent/` child transcripts are not sessions of their own; linking them as delegation is left for a follow-up, so Muse hydration reports `partial`. Usage normalizes as `per-request` with cache reads made exclusive. Grok's history re-attribution is factored into a shared helper both whole-session readers use. Fixtures include a trimmed transcript the real CLI wrote (MIT, xhluca/session-migrate), with snapshots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Warning Review limit reachedNext included review available in 26 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis change adds Muse Code as a session source. It discovers and parses Muse transcripts, captures and hydrates session evidence, indexes delegated sessions, and exposes Muse through source APIs, CLI and SDK operations, and documentation. ChangesMuse session support
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~60 minutes Change: Feature Sequence Diagram(s)sequenceDiagram
participant MuseSync
participant MuseSessionFiles
participant read_muse_tree
participant ingest_muse_session_tree
participant SessionDatabase
MuseSync->>MuseSessionFiles: Enumerate top-level transcripts and child logs
MuseSync->>read_muse_tree: Read transcript tree
read_muse_tree->>MuseSessionFiles: Read session JSONL records
MuseSync->>ingest_muse_session_tree: Replace indexed session tree
ingest_muse_session_tree->>SessionDatabase: Write history, events, and relationships
Suggested reviewers: Merge Risk: 🟡 Moderate · up to Muse sessions can record per-step token usage, model, and stop reason against the wrong assistant message, and the last message in a step can lose its usage. Usage accounting for Muse is therefore unreliable until the step pairing is fixed. Hydration metrics also under-report the bytes read when subagent logs are included. Fix the step pairing before merging. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Muse sessions gain the same searchable and resumable treatment as other sources. The new source can also follow links outside its configured session directory during discovery and sync, potentially indexing unintended readable transcripts. The risk is limited by the indexing process’s filesystem permissions; the available evidence does not establish cross-user access. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 67.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 96 functions across 20 files. (12 skipped: 11 unsupported, 1 too large.) ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit reads the session trail, Comment |
Conflicts: - change_feed.rs: main replaced `parse_source` with `Source::parse`, which covers `Source::Muse` through `Source::ALL`; take main's side. - CHANGELOG.md: keep both Rust API entries. Also adapt the Muse sync pass to main's retention gate: `report.ensure_headroom(conn, "muse")` before it and the new `report.capture(conn, …)?` signature. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict: CHANGELOG.md Rust API entries; both kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pair each model step's usage with its assistant record in either order. Muse logs `model_completed` before a tool step's calls but after a prose step's reply, so final replies were losing their usage (every text step in the real capture). Model and finish_reason now belong to the owning record only. - Guard Muse with the destination marker (REPAIRABLE_EVENT_SOURCES), so rows lost under an unchanged stamp are re-read by the next sync, including a parent whose linked subagent is short; a deregistered Muse subagent is not a catalog shortfall. - Include subagent logs in the discovery fingerprint, so a child that grows on its own is not hidden behind the sync fast path. - Read a session's whole tree before writing: an unreadable transcript or child log is that session's failure and the pass continues; a write failure (retention) still ends it. Check capture headroom per session and propagate cancellation from the stamp walk. - Hydration with include_related=false no longer reads or rewrites subagent evidence. - Only newline-terminated records are parsed. - Only a `bash` result's exit_code marks a call failed. - Catalog first_prompt / last_assistant_text are assigned from the transcript, so a rewrite does not keep stale text. - Shorten the authored fixture's session ids: the nested subagent paths exceeded Windows' MAX_PATH and broke checkout on the Windows jobs. Add `muse` to the plugin's SOURCE_CHOICES assertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ostic - A Muse session also known remotely shares its history identity with that evidence. A whole-transcript re-read no longer replaces its history, a linked subagent's objective is removed only when nothing remote stands behind the id (as for Codex), a removed child with a remote presence keeps its evidence, and the destination marker's deregistration exemption requires no remote presence. - MUSE_SUBAGENT_LOG_MISSING compares the session's own subagent_spawn calls with its direct children that it typed as workers, instead of workers at every depth plus logs it never described. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oval - A re-read removes the history rows its previous read wrote — the ones its stored prompt events name — when the session is also known remotely, instead of skipping cleanup. A prompt the transcript dropped stops being searchable; remote-only prompts stay. Sessions with no remote presence keep the whole-identity replacement. - A subagent's run-start prompt is ingested as an event only (MuseRole::Subagent), never as history, whether or not the child is known remotely. - Forgetting a removed child always forgets the children it linked from the missing directory and drops its edges to them; only the child's own events are kept when it is known remotely. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict: CHANGELOG.md Rust API entries; both kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d role - The remote-scoped history cleanup now goes through replace_session_history_scoped, so a shared prompt row another session still evidences is re-attributed instead of deleted. - A subagent with no catalog row and no events left is named for repair through its parent's muse_subagent_log edge, so the next plain sync re-reads the parent's tree and restores it. - A transcript at …/subagent/<id>/session.jsonl is always read as a subagent, including when a remotely known child is hydrated directly, so its objective never becomes history. - A removed child that is known remotely loses its local presence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- MuseProvider::read_shallow returns no row for a log at …/subagent/<id>/session.jsonl, so a watch hit or by-path read cannot catalogue a child the enumeration already skips. - A subagent log read directly (hydrating a remote child's raw_path) is deregistered from the catalog like a linked child, unless a remote presence stands behind it. - Forgetting a removed child also re-attributes or deletes history still filed under it, unless it is known remotely. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A step can commit readable reasoning and then its tool calls. The previous pairing reopened a new step at the second commit, so every later step's usage shifted one record forward and the last reply lost its own (visible in the authored fixture). Each run is now cut into segments at `started`, `tool_result_batch_committed` and `terminal` — the points after which the model is called again — and a segment's first assistant record owns its `model_completed`, in either order. A second usage-bearing completion in one segment is counted as unattached. Hydration with related evidence also reports the subagent logs it read in `bytes_read`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Devin Review found 1 new potential issue.
🐛 1 issue in files not directly in the diff
🐛 Unreadable child log erases subagent evidence
When an existing child log cannot be statted, is_file() silently omits it from the tree. The changed stamp triggers forget_muse_subagent, deleting its indexed events and relationship as though the log vanished.
5 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
direct_muse_child_logs used `is_file()`, which reads any stat error as "not a file". A child log that exists but cannot be statted (permission denied, I/O error) dropped out of the tree, the changed stamp re-read the parent, and forget_muse_subagent deleted the child's evidence and edge. Only NotFound / NotADirectory now mean absent; any other error fails that session's read, which keeps no stamp and is retried. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re Devin's out-of-diff finding "Unreadable child log erases subagent evidence" ( 🤖 Addressed by Claude Code |
What
Adds Muse Code (Meta's
museCLI) as a first-class source,muse, so its sessions go through discovery,sync, targeted hydration and live capture like every other harness.$XDG_DATA_HOME/muse/sessions/YYYY/MM/DD/<id>/session.jsonl(~/.local/share/muse/sessionsby default).ProviderRoots::muse, and the sync service now forwardsXDG_DATA_HOME.crates/ai-hist/src/ingest/muse.rsholds the record interpretation shared by discovery, sync and hydration.historyrows for typed prompts, at Muse's ownrecorded_at(µs → ms). No timestamps are inferred.session_events: prose, readable thinking,tool_useandtool_result, withrequest_id(response_id),stop_reason(finish_reason),turn_id(run_id) and agent version.tool_callsand tool-result fidelity. Status comes fromtool_batch.effect.terminaljoined by call id; abashcall with a non-zeroexit_codeis an error.file_editsforwrite_file/edit_file.model_completedusage per model step, stored verbatim on the first assistant row that step committed. It normalizes asper-request, with cache reads made exclusive of input.turn_end,session_start,session_resumed,session_end,model_switch,encrypted_reasoning.stream.idare read. Muse copies subagent and reminder task streams into the parent file, and those would otherwise show up as typed prompts.subagent/child transcripts are never enumerated as sessions.ai-hist resumeprintsmuse resume <id>.museis also added to the CLI, napi, TS SDK and MCP source lists.Why
Muse users should get the same searchable, resumable history as the other harnesses. Per the sourcing ADR, providers are added here and nowhere else.
Reviewer notes
subagent/<dir>/session.jsonlbeside its parent, possibly nested. Reading a session reads its whole tree:delegated(evidence_kind = "muse_subagent_log"). Role (worker/reminder), label, model and task id come from the parent'stask_stream_linked/memory_reminder_child_session_linkedrecord, plusspawn_depth.historyand catalog rows are removed, so it is not a typed prompt or a session of its own. Its events stay reachable through the edge andrelated_session_ids.always, and Muse hydration reportsfull.history" step moves into a sharedreplace_session_history(conn, source, session_id), which both whole-session readers now use. The SQL is equivalent:e.source = h.sourcereplaces the'grok'literal. Grok snapshots are unchanged.muse/cli-captureis a trimmed transcript that the real Muse 0.2.1 CLI wrote, taken from xhluca/session-migrate (MIT). Every kept line is byte-identical to upstream.muse/tools-sessionis authored, and covers edits, a failingbash, encrypted reasoning, a model switch, a mirrored task stream, andsubagent/logs: a worker with its own nested child, and a reminder.public-api.txt: updated by hand (cargo public-apiwasn't installed locally), addingSource::MuseandProviderRoots::muse. Both types are#[non_exhaustive], so the change is additive. CI's check will confirm it.musecolumn plus a rationale paragraph), session-catalog tables and a new Muse section, the sourcing-sdk population table, architecture, README, CHANGELOG (Added and Rust API).Merge with
mainchange_feed.rs:mainreplacedparse_sourcewithSource::parse, which coversSource::MusethroughSource::ALL.CHANGELOG.md: both Rust API entries kept.main's retention gate (ensure_headroom) and the newreport.capture(conn, …)?signature.Testing
cargo fmt --check,cargo clippy --workspace --all-targets --all-features -D warnings, andcargo build -p ai-hist --no-default-features: all clean.main. None of them are caused by this change:an_unreadable_codex_rollout_does_not_retire_the_backfill_pass: socket path overSUN_LENunder the long macOS temp dira_linked_worktree_resolves_an_onbranch_include_against_its_own_branchandglobal_and_conditional_git_config_resolve_a_rewritten_remote: git config path quirks on macOScorpus_snapshots_match_committed_evidencefor the Grok fixtures: a marker payload is cut at 128 chars, and the longer macOS temp path changes where it is cut🤖 Generated with Claude Code
Note
Medium Risk
Large new ingest/discovery surface and subagent linking behavior, but changes are additive (
#[non_exhaustive]APIs) with extensive fixture tests; risk is mainly parser correctness on real Muse log variants.Overview
Adds Muse Code (
muse) as a first-class harness: sessions under$XDG_DATA_HOME/muse/sessionsare discovered, synced, hydrated, and live-watched like other providers, withXDG_DATA_HOMEforwarded through the sync service.A shared
ingest/muse.rsparser mapssession.jsonlinto history, events, tool calls/results (outcomes fromtool_batch.effect.terminal,bashexit codes), file edits, per-step token usage, lifecycle markers, andmuse resumesupport across Rust/Node/MCP/TS.Subagent/reminder logs under
subagent/are indexed only when related evidence is requested: children get their own session id anddelegatededges, are excluded from catalog enumeration and shallow reads, and fingerprinting includes child logs so background writes still trigger re-sync.Reviewed by Cursor Bugbot for commit f93c832. Bugbot is set up for automated code reviews on this repo. Configure here.