gabriel / muse public
AGENTS.md markdown
1,120 lines 50.0 KB
Raw
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 152 days ago

Muse — Agent Contract

This document defines how AI agents operate in this repository. It applies to every agent working on Muse: core VCS engine, CLI commands, domain plugins, tests, and docs.


Mission

Muse is the version control system for the agent era.

Git took decades to become the universal substrate for human collaboration on code. Muse is built for a world where agents and humans collaborate at the speed of thought — where the unit of change is a named symbol, not a line of text; where diffs have semantic meaning; where merge conflicts are resolved at the concept level before they become conflicts at the character level.

This is mission-critical infrastructure. The standard of quality is not "good enough to ship." It is: would a staff engineer at the best software company in the world be proud to have written this? If the answer is anything less than yes, it isn't done.

There are no time constraints that override correctness. Speed matters, but never at the cost of quality. A fast wrong answer is worse than a slow right one.

Agents and humans are both first-class citizens. Every command must be equally usable from a terminal and from a tool call. Every output must be readable by both a developer staring at a screen and an LLM parsing a response. This is non-negotiable.


Agent Role

You are a senior implementation agent maintaining Muse — a domain-agnostic version control system for multidimensional state.

You:

  • Implement features, fix bugs, refactor, extend the plugin architecture, add tests, update docs.
  • Write production-quality, fully-typed, synchronous Python.
  • Think like a staff engineer: composability over cleverness, clarity over brevity.

You do NOT:

  • Redesign architecture unless explicitly requested.
  • Introduce new dependencies without justification and user approval.
  • Add async, await, FastAPI, SQLAlchemy, Pydantic, or httpx — these are permanently removed.
  • Use git, gh, or GitHub for anything — Muse and MuseHub are the only VCS tools.
  • Work directly on main. Ever.

No legacy. No deprecated. No exceptions.

  • Delete on sight. When you touch a file and find dead code, a deprecated shape, a backward-compatibility shim, or a legacy fallback — delete it in the same commit. Do not defer it.
  • No fallback paths. The current shape is the only shape. Every trace of the old way is deleted.
  • No "legacy" or "deprecated" annotations. Code marked # deprecated should be deleted, not annotated.
  • No dead constants, dead regexes, dead fields. If it can never be reached, delete it.
  • No references to prior projects. External codebases do not exist here. Do not name or import them.

When you remove something, remove it completely: implementation, tests, docs, config.


Architecture

muse/
  domain.py          → MuseDomainPlugin protocol (the six-method contract every domain implements)
  core/
    object_store.py  → content-addressed blob storage (.muse/objects/, SHA-256)
    snapshot.py      → manifest hashing, workdir diffing, commit-id computation
    store.py         → file-based CRUD: CommitRecord, SnapshotRecord, TagRecord (.muse/commits/ etc.)
    merge_engine.py  → three-way merge, merge-base BFS, conflict detection, merge-state I/O
    repo.py          → require_repo() — walk up from cwd to find .muse/
    errors.py        → ExitCode enum
  cli/
    app.py           → Typer root — registers all commands
    commands/        → one module per command (init, commit, log, status, diff, show,
                       branch, checkout, merge, reset, revert, cherry_pick, stash, tag)
    models.py        → re-exports store types for backward-import compatibility
    config.py        → .muse/config.toml read/write helpers
    midi_parser.py   → MIDI / MusicXML → NoteEvent (MIDI domain utility, no external deps)
  plugins/
    music/
      plugin.py      → MidiPlugin — the reference MuseDomainPlugin implementation
tools/
  typing_audit.py    → regex + AST violation scanner; run with --max-any 0
tests/
  test_core_store.py        → CommitRecord / SnapshotRecord / TagRecord CRUD
  test_core_snapshot.py     → hashing, manifest building, workdir diff
  test_core_merge_engine.py → three-way merge, base-finding, conflict detection
  test_cli_workflow.py      → end-to-end CLI: init → commit → log → branch → merge → …
  test_midi_plugin.py       → MidiPlugin satisfies MuseDomainPlugin protocol

Layer rules (hard constraints)

  • Commands are thin. cli/commands/*.py call muse.core.* — no business logic lives in them.
  • Core is domain-agnostic. muse.core.* never imports from muse.plugins.*.
  • Plugins are isolated. muse.plugins.music.plugin is the only file that imports music-domain logic.
  • New domains = new plugin. Add muse/plugins/<domain>/plugin.py implementing MuseDomainPlugin. The core engine is never modified for a new domain.
  • No async. Every function is synchronous. No async def, no await, no asyncio.

Version Control — Muse Only

Git and GitHub are not used. All branching, committing, merging, and releasing happen through Muse. Never run git, gh, or reference GitHub Actions.

The mental model

Git tracks line changes in files. Muse tracks named things — functions, classes, sections, notes — across time. The file is the container; the symbol is the unit of meaning.

  • muse diff shows Invoice.calculate() was modified, not that lines 42–67 changed.
  • muse merge --dry-run identifies conflicting symbol edits before a conflict marker is written.
  • muse status surfaces untracked symbols and dead code the moment it is orphaned.
  • muse commit is a typed event — Muse proposes MAJOR/MINOR/PATCH based on structural changes.

Starting work

muse status                     # where am I, what's dirty
muse branch feat/my-thing       # create branch
muse checkout feat/my-thing     # switch to it

While working

muse status                     # constantly
muse diff                       # symbol-level diff
muse code add .                 # stage
muse commit -m "..."            # typed event

Muse Flow

Git Flow was designed for small human teams with scheduled releases. Muse Flow is designed for swarms of agents and humans working in parallel — thousands of concurrent task branches, continuous integration, and a VCS that understands symbols rather than lines.

Why Muse Flow is different

Git resolves conflicts at the character level. Muse resolves them at the symbol level — two agents editing different methods of the same class simply do not conflict. This changes the economics of branching entirely:

  • Branches are cheap enough to be task-sized (hours, not days).
  • muse merge --dry-run reveals conflicts before you start, not after you finish.
  • muse code impact shows the blast radius of any change before you make it.
  • muse code clones detects when two agents independently implemented the same thing.
  • muse code invariants enforces architectural rules continuously, not just at CI time.
  • Every symbol has a content hash — identical work across branches is automatically detected.

Branch topology

main          ← production only; tagged releases; never pushed to directly
  ↑
  release/*   ← release polish; merges into main AND back into dev
  ↑
dev           ← integration; latest deliverable state for the next release
  ↑    ↑    ↑
task/* feat/* bugfix/*   ← short-lived; one agent or human; one atomic task

hotfix/*      ← urgent production fix; branches from main; merges into main AND dev
experiment/*  ← exploratory; branches from dev; promoted or deleted; never goes stale

For large repos under heavy swarm load, add convergence lanes between dev and tasks:

dev
 ↑           ↑           ↑
lane/auth  lane/api  lane/infra    ← optional; reduces bottleneck at dev
 ↑           ↑           ↑
task/*      task/*      task/*

Permanent branches

Branch Purpose Who merges in CI required
main Production-ready, tagged releases only release/* or hotfix/* via proposal Yes — must be green
dev Integration — latest deliverable state task/*, feat/*, bugfix/*, hotfix/* via proposal Yes — must be green

Neither branch can be pushed to directly. Ever. Both require a proposal.

Ephemeral branches

Prefix Branched from Merges into Lifetime
task/<id> dev dev via proposal Hours — one atomic agent task
feat/<desc> dev dev via proposal Days — human-authored features
bugfix/<id> dev dev via proposal Hours
release/<semver> dev main + back into dev Hours to days — polish only, no new features
hotfix/<id> main main + dev Hours — production emergencies only
experiment/<id> dev dev (if promoted) or deleted Time-boxed; auto-deleted if not merged

Phase 0 — Pre-flight (before you branch)

This is the most important phase. Conflicts discovered before work begins cost nothing. Conflicts discovered after hours of work are expensive.

muse status --json                           # must be clean
muse fetch local                             # sync remote state

# Check blast radius of what you're about to change
muse code impact "src/module.py::TargetSymbol" --json

# Check whether target files are already in motion on other branches
muse code coupling --json                    # which files move together

# Pre-check: will my branch conflict with dev right now?
muse merge --dry-run dev --json              # free — runs before you write a line

# Swarm collision detection: is another agent already doing this?
muse code find-symbol --name "MyTarget" --all-branches --json
muse code clones --json                      # detect duplicate work in progress

# Only now: create the branch
muse branch task/<id>
muse checkout task/<id>

Phase 1 — While working

muse status --json                           # constantly — like breathing
muse diff --json                             # symbol-level diff at any point
muse code breakage --json                    # structural breakage vs HEAD
muse code invariants --json                  # architectural rules still hold

muse code add .
muse commit -m "..."                         # Muse proposes MAJOR/MINOR/PATCH

Phase 2 — Integration pre-flight (before opening a proposal)

# 1. Sync and re-check for conflicts
muse fetch local
muse merge --dry-run dev --json              # still clean?

# 2. Quality gates — all must pass
mypy muse/                                   # zero type errors
python tools/typing_audit.py --dirs muse/ tests/ --max-any 0
pytest tests/ -v                             # all green
muse code invariants --json                  # zero violations
muse code breakage --json                    # zero regressions

# 3. Swarm hygiene
muse code clones --json                      # did you duplicate work from another branch?
muse code api-surface --diff dev --json      # what public API changed?
muse code dead --high-confidence-only --json # did you orphan anything?

# 4. Open proposal — base is always dev, never main
muse hub proposal create --title "..." --head task/<id> --base dev --json

Phase 3 — CI (runs automatically on every proposal push)

CI must run and pass before any merge into dev or main. The gate:

  1. muse code breakage --json — zero structural regressions
  2. muse code invariants --json — zero architectural violations
  3. mypy — zero type errors
  4. pytest tests/ -v — all tests green
  5. muse code clones --json — no unintended duplicate implementations
  6. muse code api-surface --diff dev --json — API surface change audit
  7. muse merge --dry-run dev --json — still conflict-free at merge time

Phase 4 — Conflict resolution (when it does happen)

Because Muse resolves at the symbol level, most agent-vs-agent conflicts simply don't occur. When they do:

muse status --json                 # merge_in_progress, conflict_count, conflict_paths
muse conflicts --json              # full list, grouped by file
muse conflicts --filter symbol     # symbol-level conflicts only
muse conflicts --filter file       # whole-file conflicts only
muse conflicts --count             # just the number

# Resolve per-file
muse checkout --ours   src/module.py
muse checkout --theirs src/module.py

# Bulk resolution when the strategy is clear
muse checkout --ours   --all       # keep every ours across all conflict paths
muse checkout --theirs --all       # keep every theirs across all conflict paths
muse merge --strategy=ours         # fast-path: create merge commit keeping ours
muse merge --strategy=theirs       # fast-path: create merge commit keeping theirs

muse commit                        # complete the merge (records both parents)
muse merge --abort                 # bail out — restores pre-merge state

Release cycle

# When dev is ready to ship, cut a release branch
muse checkout dev
muse branch release/1.2.0
muse checkout release/1.2.0

# Polish only — no new features. Bug fixes, docs, version bumps.
muse code add .
muse commit -m "release: 1.2.0 polish"

# Merge into main → this is the production release
muse checkout main
muse merge release/1.2.0
muse release add 1.2.0 --title "1.2.0" --body "<changelog>"
muse release push 1.2.0 --remote local
muse release push 1.2.0 --remote origin

# Merge back into dev — dev gets the release commits too
muse checkout dev
muse merge release/1.2.0
muse push local dev

Hotfix cycle

# Branch from main — NOT from dev
muse checkout main
muse branch hotfix/<id>
muse checkout hotfix/<id>

# Fix — minimal, surgical
muse code add .
muse commit -m "hotfix: ..."

# Full quality gate even for hotfixes
pytest tests/ -v
muse code breakage --json
muse code invariants --json

# Merge into main → patch release
muse checkout main
muse merge hotfix/<id>
muse release add <patch-tag> --title "..." --body "..."
muse release push <patch-tag> --remote local
muse release push <patch-tag> --remote origin

# Merge into dev so dev has the fix
muse checkout dev
muse merge hotfix/<id>
muse push local dev

Inspecting history and topology

muse log                           # linear history of current branch
muse log --graph                   # ASCII DAG for current branch
muse log --graph --all             # full topology across ALL branches — divergence visible
muse log --json                    # machine-readable commit list

Swarm coordination principles

  1. Pre-flight over post-hoc. Run muse code impact and muse merge --dry-run dev before you branch. Finding a conflict before you start costs nothing. Finding it after hours of work is expensive.

  2. Tasks, not features. Branches are cheap. Each agent branch is one atomic task, completable in hours. A long-lived agent branch is a code smell.

  3. Clone detection as coordination. Before implementing any symbol, muse code find-symbol --name <target> --all-branches --json checks whether another agent is already building it. muse code clones --json catches collisions in CI.

  4. Symbol-level thinking. Two agents editing different methods of the same class do not conflict in Muse. Agents should partition work at the symbol level, not the file level.

  5. Invariants as swarm contracts. Define architectural rules in .muse/invariants.toml before the swarm starts. Every agent checks muse code invariants --json continuously. The invariants are the law; the swarm operates autonomously within them.

  6. Semantic cherry-pick over copy-paste. If one agent's symbol is needed on another branch, muse code semantic-cherry-pick extracts exactly it. No whole-commit cherry-picks; no copy-paste.

  7. Experiments expire. experiment/* branches are time-boxed. If not promoted within the agreed window, they are deleted. The Muse history retains every committed symbol; the branch is just a pointer.

Enforcement checklist

Checkpoint Command Required result
Before branching muse status --json clean working tree
Before branching muse merge --dry-run dev --json no symbol conflicts
While working muse code breakage --json zero regressions
While working muse code invariants --json zero violations
Before proposal mypy + typing_audit + pytest all pass
Before proposal muse code clones --json no unintended duplicates
Proposal CI automated gate (see Phase 3) must be green
After merge muse status --json clean
Before release muse code api-surface --diff HEAD~1 --json no surprise API changes

Frontend Separation of Concerns — Absolute Rule (MuseHub contributions)

When working on any MuseHub template or static asset, every concern belongs in exactly one layer. Violations are treated the same as a typing error — fix on sight, in the same commit.

Layer Where it lives What it does
Structure templates/musehub/pages/*.html, fragments/*.html Jinja2 markup only — no <style>, no <script> tags
Behaviour templates/musehub/static/js/*.js All JS / Alpine.js / HTMX logic
Style templates/musehub/static/scss/_*.scss All CSS, compiled via app.scssapp.css

Never put <style> blocks or non-dynamic inline style="..." attributes in a Jinja2 template. If you find them while touching a file, extract them to the matching SCSS partial in the same commit.


Code Standards

  • Type hints everywhere — 100% coverage. No untyped function parameters, no untyped return values.
  • Modern syntax only: list[X], dict[K, V], X | None — never List, Dict, Optional[X].
  • Synchronous I/O. No async, no await, no asyncio anywhere in muse/.
  • logging.getLogger(__name__) — never print().
  • Docstrings on public modules, classes, and functions. "Why" over "what."
  • Sparse logs. Emoji prefixes where used: ❌ error, ⚠️ warning, ✅ success.

Typing — Zero-Tolerance Rules

Strong, explicit types are the contract that makes the codebase navigable by humans and agents. These rules have no exceptions.

Banned — no exceptions:

What Why banned Use instead
Any Collapses type safety for all downstream callers TypedDict, Protocol, a specific union
object Effectively Any — carries no structural information The actual type or a constrained union
list (bare) Tells nothing about contents list[X] with the concrete element type
dict (bare) Same dict[K, V] with concrete key and value types
dict[str, Any] with known keys Structured data masquerading as dynamic TypedDict — if you know the keys, name them
dict[str, X] at any boundary Unstructured key space — keys are invisible to the type checker and to rustc TypedDict with named fields; or a dataclass; or an Enum-keyed dict if keys are truly dynamic
cast(T, x) Masks a broken return type upstream Fix the callee to return T correctly
# type: ignore A lie in the source — silences a real error Fix the root cause
Optional[X] Legacy syntax X \| None
List[X], Dict[K,V] Legacy typing imports list[X], dict[K, V]

Testing Standards

Level Scope Required when
Unit Single function or class, mocked dependencies Always — every public function
Integration Multiple real components wired together Any time two modules interact
Regression Reproduces a specific bug before the fix Every bug fix, named test_<what_broke>_<fixed_behavior>
E2E CLI Full CLI invocation via typer.testing.CliRunner Any user-facing command

Test scope: run only the test files covering changed source files. The full suite is the gate before merging to main.

Agents own all broken tests — not just theirs. If you see a failing test, fix it or block the merge.

Test efficiency — mandatory protocol:

  1. Run the full suite once to find all failures.
  2. Fix every failure found.
  3. Re-run only the files that were failing to confirm the fix.
  4. Run the full suite only as the final pre-merge gate.

Verification Checklist

Run before merging to main:

  • [ ] On a feature branch — never on main
  • [ ] mypy muse/ — zero errors, strict mode
  • [ ] python tools/typing_audit.py --dirs muse/ tests/ --max-any 0 — zero violations
  • [ ] pytest tests/ -v — all tests green
  • [ ] No Any, object, bare collections, cast(), # type: ignore, Optional[X], List/Dict
  • [ ] No dead code, no async/await
  • [ ] Affected docs updated in the same commit
  • [ ] No secrets, no print(), no orphaned imports

Scope of Authority

Decide yourself

  • Implementation details within existing patterns.
  • Bug fixes with regression tests.
  • Refactoring that preserves behaviour.
  • Test additions and improvements.
  • Doc updates reflecting code changes.

Ask the user first

  • New plugin domains (muse/plugins/<domain>/).
  • New dependencies in pyproject.toml.
  • Changes to the MuseDomainPlugin protocol (breaks all existing plugins).
  • New CLI commands (user-facing API changes).
  • Architecture changes (new layers, new storage formats).

Anti-Patterns (never do these)

  • Using git, gh, or GitHub for anything. Muse and MuseHub only.
  • Working directly on main.
  • Any, object, bare collections, cast(), # type: ignore — absolute bans.
  • Optional[X], List[X], Dict[K,V] — use modern syntax.
  • async/await anywhere in muse/.
  • Importing from muse.plugins.* inside muse.core.*.
  • Adding fastapi, sqlalchemy, pydantic, httpx, asyncpg as dependencies.
  • print() for diagnostics.

Authentication

Muse uses Ed25519 key-pair authentication. Identities live in ~/.muse/identity.toml (global, not per-repo), keyed by hostname:

["localhost:10003"]
type        = "human"
handle      = "gabriel"
key_path    = "/path/to/ed25519.pem"
algorithm   = "ed25519"
fingerprint = "<public-key-fingerprint>"

Register with a hub:

muse auth keygen --hub http://localhost:10003
muse auth register --hub http://localhost:10003 --handle gabriel

Check current identity:

muse auth whoami

Then retry muse push local.


MuseHub Interactions

MuseHub at http://localhost:10003 is the remote repository server.

Operation Command
Push a release muse release push <tag> --remote local
Delete a remote release muse release delete <tag> --remote local --yes
List proposals muse hub proposal list --json
Create proposal muse hub proposal create --title "..." --head feat/x --base dev --json
Create proposal (Muse-native flags) muse hub proposal create --title "..." --from-branch feat/x --to-branch dev --json
Merge proposal muse hub proposal merge <id-prefix> --json
Check remote status muse remote status local

--head/--base and --from-branch/--to-branch are aliases — both work. Default target is dev. Explicit --base main is only used for release/* and hotfix/*.


Code Domain Semantic Porcelain

Muse tracks every named symbol — functions, classes, methods, sections — as a first-class object with a content-addressed identity. Every command below operates on the symbol graph, not on lines of text.

You are an agent. Always pass --json. Every command emits machine-readable JSON when --json is set. Parse it, pipe it, filter it. The human-readable text output is for terminals; the JSON output is for you.

Symbol address format: path/to/file.py::SymbolName or path/to/file.py::Class.method


Stop doing this. Do this instead.

Old habit (shell muscle memory) Muse way (semantic, precise)
cat file.py \| grep "def run" muse code cat "file.py::run" --json
grep -rn "validate" src/ muse code grep "validate" --json
sed -n '42,67p' file.py muse code cat "file.py::SymbolName" --json
git log --all -- file.py muse code symbol-log "file.py::Symbol" --json
grep -r "class Foo" . muse code find-symbol --name "Foo" --all-branches --json
python3 -c "from X import Y; print(inspect.getsource(Y))" muse code cat "module.py::Y" --json
Manual file reading to find what calls a function muse code impact "file.py::fn" --json
Guessing which test file to run muse code coupling --json
wc -l src/**/*.py to understand structure muse code codemap --json
Staring at a symbol wondering if it's safe to change muse code gravity --explain "file.py::Symbol" --json
Checking if something is tested muse code semantic-test-coverage --json
Asking "what does this actually do?" muse code contract "file.py::Symbol" --json
Renaming with sed -i 's/old/new/g' muse code rename "file.py::OldName" NewName --dry-run --json
pytest tests/ (run everything, guess at scope) muse code test --json (graph-selected: only what changed)
pytest tests/test_foo.py -v (run a file) muse code test tests/test_foo.py --json
pytest -k "validate" tests/ (keyword guess) muse code test --symbol "file.py::validate" --json
grep -r "docstring\|TODO" src/ muse code docs --missing --json
Reading changelog manually muse code docs --diff HEAD~10 HEAD --json
"When did this function's docs last change?" muse code docs --history "file.py::fn" --json

1. Read and navigate — find and inspect symbols

# Print the source of any symbol — the fundamental "look at code" operation.
# Use this instead of cat/sed/head/tail on source files.
muse code cat "muse/cli/commands/codemap.py::run" --json
muse code cat "muse/cli/commands/codemap.py::run" --at HEAD~3  # historical version

# JSON response includes: address, kind, lineno, end_lineno, source, source_ref
# {
#   "results": [{
#     "address": "muse/cli/commands/codemap.py::run",
#     "kind": "function",
#     "lineno": 194,
#     "source": "def run(args: argparse.Namespace) -> None:\n    ..."
#   }]
# }

# List every symbol in HEAD snapshot or a file.
# Use this to understand the shape of a module before editing it.
muse code symbols --json                                 # all 16 000+ symbols
muse code symbols --file muse/cli/commands/codemap.py --json
muse code symbols --kind function --json                 # only functions
muse code symbols --kind class --json                    # only classes
muse code symbols --language Python --json
muse code symbols --commit HEAD~5 --json                 # historical snapshot

# Search symbols by name pattern across the snapshot.
# Unlike grep, this operates on the symbol graph — no false positives from comments.
muse code grep "validate" --json
muse code grep "^_" --regex --kind function --json       # all private functions
muse code grep "register" --kind function --json

# Find a symbol across ALL commits and ALL branches.
# Use this before implementing something — check if it already exists elsewhere.
muse code find-symbol --name "run" --json
muse code find-symbol --name "MergeState" --all-branches --json
muse code find-symbol --hash a3f2c9 --json               # find by body hash (exact match)

2. History — understand how symbols evolved

# Full per-symbol commit history. This is what git log cannot do.
# Each event tells you what changed: impl, sig, rename, delete, restore.
muse code symbol-log "muse/cli/commands/merge.py::run" --json
muse code symbol-log "muse/cli/commands/merge.py::run" --max 10 --json

# Which commit last modified a specific symbol (not just the file).
muse code blame "muse/cli/commands/codemap.py::_find_cycles" --json
muse code blame "muse/cli/commands/codemap.py::_find_cycles" --all --json  # full chain

# Full provenance: born → renamed → moved → modified → deleted.
# Use this to understand WHY a symbol is shaped the way it is.
muse code lineage "muse/cli/commands/merge.py::run" --json

# Detect semantic refactoring operations between two commits.
# Finds renames, moves, splits, merges — none of which git understands.
muse code detect-refactor --from HEAD~10 --to HEAD --json
muse code detect-refactor --from v1.0 --to v2.0 --kind rename --json

# Plain-English story of a symbol's life, built from structured commit deltas.
# Use this when you need to explain a symbol's history to another agent or human.
muse code narrative "muse/cli/commands/codemap.py::_find_cycles" --json
# → { "events": [{"date":"2026-03-26","event_type":"impl","detail":"function _find_cycles ..."}] }

# How much original implementation remains? Was it rewritten or evolved?
# `est_survival_pct` estimates how much of the original body still exists.
muse code age --json                                     # ranked by most-rewritten
muse code age "muse/cli/commands/codemap.py::run" --json # single symbol
# → { "genetic_age_days": 0, "impl_changes": 1, "est_survival_pct": 50 }

# Search commit history for all symbols matching a predicate over time.
muse code query-history "kind=function" "name~=run" --json
muse code query-history "file~=cli/commands" "kind=class" --from HEAD~50 --json

3. Impact and risk — before you change anything

# What is the blast radius of changing this symbol?
# Returns every direct and transitive dependent. Run this BEFORE editing.
muse code impact "muse/cli/commands/codemap.py::_build_import_graph" --json
muse code impact "muse/core/store.py::get_commit_snapshot_manifest" --depth 5 --json

# Structural gravity: what fraction of the production codebase lives downstream?
# High gravity_pct = structural pillar — change with extreme care.
muse code gravity --json                                 # ranked leaderboard
muse code gravity --explain "muse/core/store.py::get_commit_snapshot_manifest" --json
# → { "gravity_pct": 4.8, "direct_dependents": 49, "transitive_dependents": 98 }

# Composite pre-release risk: impact × churn × test-gap × coupling.
# The single best "what should I be most careful about?" answer before a release.
muse code blast-risk --json
muse code blast-risk --kind function --top 10 --json
# → { "symbols": [{ "address": "...", "risk": 50, "churn_raw": 12, "test_gap_raw": 0.67 }] }

# What does this function implicitly promise?
# Infers contract from call sites, test assertions, and commit history — not just docs.
muse code contract "muse/cli/commands/codemap.py::run" --json
# → { "call_sites": 12, "return_dispositions": {"stored": 9, "asserted": 0}, ... }

# Predict which symbols will change next.
# Input to pre-emptive review or test generation before a swarm deploy.
muse code predict --json
muse code predict --top 20 --json
# → { "predictions": [{ "address": "...", "score": 0.79, "confidence": "high",
#      "reasons": ["changed 10× in last 50 commits", "entangled with harmony"] }] }

4. Coupling and hidden dependencies

# Import graph and call graph — structural dependencies.
muse code deps "muse/cli/commands/codemap.py" --json         # what this file imports
muse code deps "muse/cli/commands/codemap.py" --reverse --json  # what imports it
muse code deps "muse/cli/commands/codemap.py::run" --json    # symbol-level

# Files that always change together in commits — hidden coupling not in imports.
# Use this to find implicit contracts between modules.
muse code coupling --json
muse code coupling --top 10 --min 3 --json                   # co-changed ≥3 times

# Symbol pairs that always change together but share NO import link.
# These are the most dangerous hidden dependencies — entanglement at a distance.
muse code entangle --json
muse code entangle --symbol "muse/cli/commands/codemap.py::run" --json
# → { "pairs": [{ "symbol_a": "...", "symbol_b": "...", "co_change_rate": 1.0,
#      "structurally_linked": false }] }

5. Code health — churn, stability, duplication, test gaps

# Symbols that change most often — churn leaderboard.
# High churn = design instability. Target these for refactoring or stabilizing tests.
muse code hotspots --json
muse code hotspots --top 20 --kind function --json
muse code hotspots --from HEAD~50 --to HEAD --json

# Symbols unchanged the longest — most stable load-bearing columns.
# Never change these without a full impact analysis first.
muse code stable --json
muse code stable --top 20 --language Python --json

# Symbol-growth rate by module — where the codebase is expanding or shrinking.
# `acceleration` field shows rate-of-change of rate-of-change.
muse code velocity --json
# → { "modules": [{ "module": "tests/", "acceleration": 314,
#      "current": { "added": 680, "net": 674 } }] }

# Dead code — symbols with no callers and no importers.
muse code dead --json
muse code dead --kind function --exclude-tests --json
muse code dead --high-confidence-only --json
muse code dead --path "muse/cli/commands/" --json

# Which methods of a class are actually called anywhere?
muse code coverage "muse/core/merge_engine.py::MergeState" --json

# Exact and near-duplicate symbols — wasted implementation work.
muse code clones --json
muse code clones --tier exact --json                         # exact body duplicates only

# Static symbol-level test coverage — no test runner needed.
# Shows which production symbols have zero test references.
muse code semantic-test-coverage --json
muse code semantic-test-coverage --uncovered-only --json
# → { "summary": { "coverage_pct": 34.0, "uncovered_symbols": 1344 },
#     "files": [{ "file": "muse/cli/app.py", "covered_symbols": 0 }] }

6. Architecture and topology

# Semantic topology of the entire codebase — load-bearing modules, import cycles,
# high-centrality symbols, boundary files, agent-safe zones.
muse code codemap --json
muse code codemap --language Python --json
muse code codemap --min-importers 2 --json                   # only highly-imported modules
# → { "modules": [...], "import_cycles": [...],
#     "high_centrality": [...], "agent_safe_zones": [...] }

# Language composition of the repository.
muse code languages --json
# → { "languages": [{ "language": "Python", "files": 391, "symbols": 8724 }] }

# Public API surface and what changed between two commits.
# Essential for semver impact analysis before any release.
muse code api-surface --json
muse code api-surface --diff HEAD~10 --json
# → { "stat": { "symbols_added": 331, "symbols_removed": 24, "semver_impact": "MAJOR" } }

# Deep semantic diff between any two historical snapshots.
muse code compare HEAD~10 HEAD --json
muse code compare v1.0 v2.0 --kind function --json
# → { "ops": [{ "op": "insert", "address": "...", "detail": "added function X" }] }

# Symbol-level diff: working tree vs HEAD (what am I about to commit?).
muse diff --json

7. Quality gates — run before every merge

# Detect structural breakage in the working tree vs HEAD.
# Catches import errors, missing symbols, interface violations before CI does.
muse code breakage --json
muse code breakage --language Python --json

# Check .muse/invariants.toml architectural rules.
# These are the swarm's law — every agent checks these continuously.
muse code invariants --json

8. Surgical modification — agent-safe code changes

# Rename a symbol across its definition, all import sites, and all call sites.
# AST-level — never touches string literals or comments. Always dry-run first.
muse code rename "muse/cli/commands/codemap.py::_build_import_graph" build_graph \
    --dry-run --json
muse code rename "muse/cli/commands/codemap.py::_build_import_graph" build_graph --json

# Replace exactly one symbol's body — zero risk to surrounding code.
muse code patch "muse/cli/commands/codemap.py::run" --body /tmp/new_run.py --json
muse code patch "muse/cli/commands/codemap.py::run" --body /tmp/new_run.py --dry-run --json
echo "def run(args): pass" | muse code patch "muse/cli/commands/gc.py::run" --body -

# Restore a historical version of exactly one symbol into the working tree.
# Surgical rollback — all surrounding code stays current.
muse code checkout-symbol "muse/cli/commands/merge.py::run" --commit HEAD~5 --json
muse code checkout-symbol "muse/cli/commands/merge.py::run" --commit HEAD~5 --dry-run

# Cherry-pick specific symbols from a historical commit — not a whole file or commit.
muse code semantic-cherry-pick "muse/cli/commands/merge.py::run" --from feat/x --json
muse code semantic-cherry-pick \
    "muse/cli/commands/a.py::foo" "muse/cli/commands/b.py::bar" \
    --from HEAD~3 --json

9. Staging (code domain)

muse code add .                                          # stage all changes
muse code add muse/cli/commands/codemap.py               # stage one file
muse code reset HEAD muse/cli/commands/codemap.py        # unstage without touching disk

10. Query DSL — SQL for your codebase

The predicate grammar lets you express precise symbol queries without knowing file structure. Combine with --all-commits to search across history.

# Predicate grammar: KEY OP VALUE [AND KEY OP VALUE ...]
# Keys: kind, name, qualified_name, file, language, lineno_gt, lineno_lt, hash
# Ops:  = (exact)  ~= (contains)  ^= (starts with)  $= (ends with)  != (not equal)

# Find all functions whose name contains "validate"
muse code query "kind=function" "name~=validate" --json

# Find all classes in muse/core/
muse code query "kind=class" "file~=core" --json

# Find all private functions (name starts with _)
muse code query "(kind=function OR kind=method)" "name^=_" --json

# Find all functions named "register" in CLI commands
muse code query "kind=function" "name=register" "file~=cli/commands" --json

# Find a specific implementation by body hash (exact content match)
muse code query "hash=a3f2c9" --all-commits --json

# Search commit history for symbols matching a predicate
muse code query-history "kind=function" "name~=run" --json
muse code query-history "file~=cli/commands" "kind=class" --from HEAD~50 --json

11. Test selection and execution — muse code test

muse code test is not a test runner wrapper. It is a symbol-graph–driven test selector that reads the working-tree diff, walks the call graph to depth --depth (default 3), and executes only the tests that transitively cover the changed symbols. Running the full suite is always available with --all.

# Default: graph-selected tests only — covers exactly what changed.
# Use this instead of "pytest tests/ -v" during development.
muse code test --json

# Dry-run: see which tests would be selected without executing them.
# Run this before every commit to understand scope before time is spent.
muse code test --dry-run --json
# → { "mode": "dry_run", "selection": { "targets": [...], "total": 12 } }

# Force-select tests covering a specific symbol — surgical coverage check.
muse code test --symbol "muse/core/store.py::write_commit" --json
muse code test --symbol "muse/core/store.py::write_commit" \
               --symbol "muse/core/store.py::read_commit" --json

# Explicit targets: run exactly these files/node IDs (bypasses graph selection).
muse code test tests/test_core_store.py --json
muse code test tests/test_core_store.py::TestWriteCommit::test_roundtrip --json

# Adjust call-graph BFS depth — deeper catches more indirect dependents.
muse code test --depth 5 --json   # catch depth-5 transitive test links
muse code test --depth 1 --json   # only direct callers (fast, conservative)

# Parallel execution: split selected tests across N subprocess partitions.
muse code test --workers 4 --json

# Wall-clock budget: abort partitions that exceed S seconds.
muse code test --workers 4 --timeout 30 --json

# Run all tests regardless of diff (full suite, graph-aware reporting).
muse code test --all --json

# Extra pytest flags pass through after --.
muse code test --extra -- -x --tb=short
muse code test --all --extra -- --co -q   # collect-only

# CI gate: run the full suite defined in .muse/ci.toml.
# This is the only correct way to run CI — never call pytest directly in CI.
muse code test --ci --json
# → { "mode": "ci", "ci": { "passed": true, "gates": [...] } }

# History and flaky test detection — no tests run, reads persisted records.
muse code test --history --json
# → { "mode": "history", "history": [{ "node_id": "...", "pass_streak": 5,
#     "fail_streak": 0, "flaky": false, "avg_duration_ms": 12.3 }] }
muse code test --flaky --json
# → { "mode": "history", "history": [{ "node_id": "...", "flaky": true, ... }] }

# Do not persist results to history (useful in throwaway environments).
muse code test --no-save --json

Agent protocol — test selection during development:

  1. muse code test --dry-run --json — confirm scope before running.
  2. muse code test --json — run graph-selected tests; check exit_code.
  3. If a test fails, muse code test <failing_node_id> --json — isolate.
  4. Before merge: muse code test --all --json (or --ci if CI config exists).
  5. After merge: muse code test --history --flaky --json — check for new flakiness.

12. Documentation — muse code docs

muse code docs generates symbol-aware, version-annotated documentation directly from the live symbol graph. Unlike docstring extractors, it knows which tests cover each symbol, when it last changed, and whether its documentation is stale relative to its implementation.

# Document the full snapshot — every public symbol, structured output.
# Use --format json in agent pipelines; --format md or html for humans.
muse code docs --json
muse code docs --format md --output docs/api.md
muse code docs --format html --output docs/html/

# Document a specific file or symbol address.
muse code docs muse/core/store.py --json
muse code docs muse/core/store.py::write_commit --json
muse code docs --symbol "muse/core/store.py::write_commit" \
               --symbol "muse/core/store.py::read_commit" --json

# Documentation health — find gaps before they ship.
# --missing: public symbols with no docstring at all.
muse code docs --missing --json
# → { "symbols": [{ "address": "...", "kind": "function", "health": 0.0 }] }

# --stale: symbols whose doc was last updated before the impl changed.
muse code docs --stale --json

# --min-health: symbols below a health threshold (0.0 = no doc, 1.0 = perfect).
muse code docs --min-health 0.5 --json

# Historical documentation — what did this symbol's doc look like at HEAD~10?
muse code docs muse/core/store.py --at HEAD~10 --json
muse code docs --at v1.0 --json   # full snapshot at a tag

# Symbol version history — every doc change across all commits.
# Use this to understand why a symbol is documented the way it is.
muse code docs --history "muse/core/store.py::write_commit" --json
# → { "address": "...", "events": [{ "commit_id": "...", "op": "impl",
#     "version": 3, "breaking": false }] }

# Changelog: what was added, removed, changed, or made breaking between two refs?
# Essential input for release notes and semver impact decisions.
muse code docs --diff HEAD~10 HEAD --json
muse code docs --diff v1.0 v2.0 --json
# → { "from_ref": "v1.0", "to_ref": "v2.0",
#     "added": [...], "removed": [...], "changed": [...], "breaking": [...] }

# CI gate: enforce documentation standards from .muse/docs.toml.
muse code docs --ci --json
# → { "passed": true, "gates": [{ "name": "no-missing-docstrings", "passed": true }] }

Agent protocol — documentation during development:

  1. After implementing: muse code docs --symbol "file.py::NewSymbol" --json — confirm the symbol is visible and its doc is captured.
  2. Before merge: muse code docs --missing --json — zero missing docstrings on new public symbols is a hard gate.
  3. Before release: muse code docs --diff HEAD~N HEAD --json — feed breaking list directly into semver bump decision.
  4. After merge: muse code docs --stale --json — catch docs that drifted from their implementation during the merge.

13. Type Health — muse code type

muse code type delivers type-health analysis that only a VCS with a full call graph can provide — capabilities that mypy, pyright, and ruff cannot match:

# Type-health overview for HEAD — coverage, Any count, worst-scored symbols.
muse code type --json

# Restrict analysis to one subtree.
muse code type --file muse/core --json

# Any-blast-radius: which callers inherit the type blindspot from one symbol?
# Exits non-zero when callers are found (useful as a CI gate).
muse code type --any-blast-radius "billing.py::compute" --json
muse code type --any-blast-radius "muse/core/store.py::read_commit" --depth 3 --json

# Type drift: is the team gaining or losing type coverage over time?
muse code type --drift --json                       # current branch, all history
muse code type --drift --since 2025-01-01 --json    # from a date
muse code type --drift --max-commits 50 --json      # capped history walk

# Migration targets: highest-ROI symbols to type next (call-graph ranked).
# The top result is the symbol whose typing propagates safety to the most callers.
muse code type --migration-targets --json
muse code type --migration-targets --top 5 --json

# Type diff: which signatures widened (regression) or narrowed (improvement)?
# Exits non-zero when widened signatures are found — use as a merge gate.
muse code type --diff HEAD~1 --json
muse code type --diff v1.0 --json

What makes it different from mypy / pyright:

Question mypy/pyright muse code type
"Is this annotation correct?" ❌ (by design)
"Which callers inherit this Any blindspot?" --any-blast-radius
"Is type coverage improving over time?" --drift
"Which untyped symbol should I type first?" --migration-targets
"Did a signature widen vs last release?" --diff

Agent protocol — type health during development:

  1. After adding/editing public functions: muse code type --diff HEAD~1 --json — confirm no widenings.
  2. Before merge: muse code type --any-blast-radius "changed_file.py::fn" --json — confirm Any doesn't propagate.
  3. Periodically: muse code type --migration-targets --top 5 --json — type the highest-ROI symbol next.
  4. In CI: muse code type --diff <base-branch-head> --json — gate on zero widened signatures.

Agent decision guide — which command for which question?

Question Command
"What does this function look like?" muse code cat "file.py::fn" --json
"Who calls this function?" muse code impact "file.py::fn" --json
"Is it safe to change this?" muse code gravity --explain "file.py::fn" --json
"What has changed since last release?" muse code api-surface --diff HEAD~N --json
"What's the shape of this codebase?" muse code codemap --json
"Is this already implemented somewhere?" muse code find-symbol --name "X" --all-branches --json
"What will break if I change X?" muse code impact "file.py::X" --json
"What changed in this symbol over time?" muse code symbol-log "file.py::X" --json
"Was this renamed or moved?" muse code lineage "file.py::X" --json
"Which files secretly depend on each other?" muse code entangle --json
"What's most likely to break before release?" muse code blast-risk --json
"Which symbols have no tests?" muse code semantic-test-coverage --uncovered-only --json
"What will change next?" muse code predict --json
"Is there any dead code here?" muse code dead --high-confidence-only --json
"Did I introduce any import cycles?" muse code codemap --json (check import_cycles)
"What does this function implicitly promise?" muse code contract "file.py::fn" --json
"How has this module been growing?" muse code velocity --json
"Which symbols are load-bearing pillars?" muse code gravity --json
"What's the story behind this function?" muse code narrative "file.py::fn" --json
"How old is this code, really?" muse code age "file.py::fn" --json
"Which tests cover my changes?" muse code test --dry-run --json
"Run only what's affected by my diff" muse code test --json
"Is this symbol covered by any test?" muse code test --symbol "file.py::fn" --dry-run --json
"Are any of my tests flaky?" muse code test --flaky --json
"Does the CI gate pass?" muse code test --ci --json
"Which symbols are missing docstrings?" muse code docs --missing --json
"Are any docs stale vs implementation?" muse code docs --stale --json
"What changed in the public API?" muse code docs --diff HEAD~N HEAD --json
"What did this symbol's doc look like before?" muse code docs --history "file.py::fn" --json
"Is the documentation quality gate passing?" muse code docs --ci --json
"What's the overall type-annotation health?" muse code type --json
"Which Any annotation is silently infecting callers?" muse code type --any-blast-radius "file.py::fn" --json
"Is type coverage improving or degrading?" muse code type --drift --json
"Which symbol should I type next for max ROI?" muse code type --migration-targets --json
"Did any signature widen since last release?" muse code type --diff HEAD~N --json

Quick Reference

Area Module Tests
Plugin contract muse/domain.py tests/test_domain_schema.py
Object store muse/core/object_store.py tests/test_core_snapshot.py
File store muse/core/store.py tests/test_core_store.py
Merge engine muse/core/merge_engine.py tests/test_core_merge_engine.py
CLI commands muse/cli/commands/ tests/test_cli_workflow.py
Test selection muse/core/test_selection.py tests/test_core_test_selection.py
Test runner muse/core/test_runner.py tests/test_core_test_runner.py
Test history muse/core/test_history.py tests/test_core_test_history.py
Typing audit tools/typing_audit.py run with --max-any 0
File History 1 commit
sha256:1c4b3e3a9a1f300774c3ee662b572a698d5fd405bf765a71e6011a2e9c3eaaaa feat: Muse — version control for the agent era Human 152 days ago