code-domain.md
markdown
| 1 | # Code Domain — Complete Reference |
| 2 | |
| 3 | > **Engine:** `muse/plugins/code/` · **No external deps for core analysis** |
| 4 | > **Scope:** Every command, module, type, and protocol in the code domain plugin |
| 5 | |
| 6 | --- |
| 7 | |
| 8 | ## Overview |
| 9 | |
| 10 | The code domain plugin treats a codebase as a **typed, content-addressed symbol graph** — not as a bag of text lines. Every function, class, method, variable, and import becomes a `SymbolRecord` with a stable content-addressed identity (SHA-256). This unlocks operations that are structurally impossible in Git: |
| 11 | |
| 12 | - Track a function through renames and cross-file moves with perfect identity. |
| 13 | - Cherry-pick a single named function out of a historical commit. |
| 14 | - Detect exact and near-duplicate code across an entire snapshot in O(1). |
| 15 | - Predict merge conflicts before writing a single byte. |
| 16 | - Enforce architectural invariants as committed rules. |
| 17 | - Assign semantic version bumps automatically at commit time. |
| 18 | - Coordinate thousands of parallel agents without a central lock server. |
| 19 | |
| 20 | --- |
| 21 | |
| 22 | ## Contents |
| 23 | |
| 24 | 1. [Symbol Identity Model](#1-symbol-identity-model) |
| 25 | 2. [Provenance & Topology Commands](#2-provenance--topology-commands) |
| 26 | 3. [Query & Temporal Search](#3-query--temporal-search) |
| 27 | 4. [Index Infrastructure](#4-index-infrastructure) |
| 28 | 5. [Symbol Identity Detail](#5-symbol-identity-detail) |
| 29 | 6. [Multi-Agent Coordination Layer](#6-multi-agent-coordination-layer) |
| 30 | 7. [Merge Engine & Architectural Enforcement](#7-merge-engine--architectural-enforcement) |
| 31 | 8. [Semantic Versioning](#8-semantic-versioning) |
| 32 | 9. [Call-Graph Tier Commands](#9-call-graph-tier-commands) |
| 33 | 10. [Architecture Internals](#10-architecture-internals) |
| 34 | 11. [Type Reference](#11-type-reference) |
| 35 | |
| 36 | --- |
| 37 | |
| 38 | ## 1. Symbol Identity Model |
| 39 | |
| 40 | Every symbol carries four content-addressed hashes and two stable keys: |
| 41 | |
| 42 | | Field | Description | |
| 43 | |---|---| |
| 44 | | `content_id` | SHA-256 of the full normalized AST (signature + body + metadata). Two symbols are identical iff their `content_id` matches. | |
| 45 | | `body_hash` | SHA-256 of the function/class body only (excluding signature and decorators). Matches across renames and decorator changes. | |
| 46 | | `signature_id` | SHA-256 of the normalized parameter list and return annotation. Matches across implementation-only changes. | |
| 47 | | `metadata_id` *(v2)* | SHA-256 of decorator list + async flag + base classes. Matches when only the implementation or signature changed. | |
| 48 | | `canonical_key` *(v2)* | `{file}#{scope}#{kind}#{name}#{lineno}` — stable machine handle for agent-to-agent symbol handoff. | |
| 49 | | `qualified_name` | Dotted path within the file (e.g. `MyClass.my_method`). | |
| 50 | |
| 51 | ### Exact Refactor Classification |
| 52 | |
| 53 | Two symbols are classified by comparing their four hashes: |
| 54 | |
| 55 | | Classification | Condition | |
| 56 | |---|---| |
| 57 | | `unchanged` | `content_id` matches | |
| 58 | | `rename` | `body_hash` matches, name differs, same file | |
| 59 | | `move` | `content_id` matches, different file, same name | |
| 60 | | `rename+move` | `body_hash` matches, different file, different name | |
| 61 | | `signature_only` | `body_hash` matches, `signature_id` differs | |
| 62 | | `impl_only` | `signature_id` matches, `body_hash` differs | |
| 63 | | `metadata_only` | `body_hash` + `signature_id` match, `metadata_id` differs | |
| 64 | | `full_rewrite` | Both signature and body changed | |
| 65 | |
| 66 | --- |
| 67 | |
| 68 | ## 2. Provenance & Topology Commands |
| 69 | |
| 70 | ### `muse lineage ADDRESS` |
| 71 | |
| 72 | Full provenance chain of a named symbol from its first appearance to the present. |
| 73 | |
| 74 | ``` |
| 75 | muse lineage src/billing.py::compute_total |
| 76 | muse lineage src/billing.py::compute_total --commit HEAD~10 |
| 77 | muse lineage src/billing.py::compute_total --json |
| 78 | ``` |
| 79 | |
| 80 | **How it works:** Walks all commits in chronological order, scanning `InsertOp`/`DeleteOp`/`ReplaceOp` entries in each `structured_delta`. Rename detection uses `content_id` matching across Insert+Delete pairs within a single commit. |
| 81 | |
| 82 | **Output events:** `created`, `modified`, `renamed_from`, `moved_from`, `deleted`. |
| 83 | |
| 84 | **Flags:** |
| 85 | - `--commit REF` — stop history walk at this commit (default: HEAD) |
| 86 | - `--json` — emit a JSON array of event objects |
| 87 | |
| 88 | **JSON schema:** |
| 89 | ```json |
| 90 | [ |
| 91 | { |
| 92 | "event": "created", |
| 93 | "commit_id": "a1b2c3d4...", |
| 94 | "committed_at": "2026-01-01T00:00:00+00:00", |
| 95 | "message": "Initial commit", |
| 96 | "address": "src/billing.py::compute_total", |
| 97 | "content_id": "sha256..." |
| 98 | } |
| 99 | ] |
| 100 | ``` |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ### `muse api-surface` |
| 105 | |
| 106 | Public API surface of a snapshot — every non-underscore function, class, and method. |
| 107 | |
| 108 | ``` |
| 109 | muse api-surface |
| 110 | muse api-surface --commit v1.0 |
| 111 | muse api-surface --diff v1.0 |
| 112 | muse api-surface --json |
| 113 | ``` |
| 114 | |
| 115 | **With `--diff REF`:** Shows three sections — **Added** (new public symbols), **Removed** (deleted public symbols), **Changed** (same address, different `content_id`). |
| 116 | |
| 117 | **Public** means: `kind` in `{function, class, method, async_function}` and `name` not starting with `_`. |
| 118 | |
| 119 | --- |
| 120 | |
| 121 | ### `muse codemap` |
| 122 | |
| 123 | Semantic topology of the entire codebase at a snapshot. |
| 124 | |
| 125 | ``` |
| 126 | muse codemap |
| 127 | muse codemap --top 10 |
| 128 | muse codemap --commit HEAD~5 |
| 129 | muse codemap --json |
| 130 | ``` |
| 131 | |
| 132 | **What it shows:** |
| 133 | - **Modules by size** — ranked by symbol count |
| 134 | - **Import graph** — in-degree (how many modules import this one) |
| 135 | - **Cycles** — import cycles detected via DFS (a hard architectural smell) |
| 136 | - **High-centrality symbols** — functions called from many places (blast-radius risk) |
| 137 | - **Boundary files** — high fan-out (imports many), zero fan-in (nothing imports them) |
| 138 | |
| 139 | **Flags:** |
| 140 | - `--top N` — show top N entries per section (default: 5) |
| 141 | - `--commit REF` — snapshot to analyse |
| 142 | - `--json` — structured output |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | ### `muse clones` |
| 147 | |
| 148 | Find exact and near-duplicate symbol clusters across the snapshot. |
| 149 | |
| 150 | ``` |
| 151 | muse clones |
| 152 | muse clones --tier exact |
| 153 | muse clones --tier near |
| 154 | muse clones --tier both |
| 155 | muse clones --commit HEAD~3 --json |
| 156 | ``` |
| 157 | |
| 158 | **Exact clones:** Same `body_hash` at different addresses. These are literal copy-paste duplicates — same implementation, possibly different name. |
| 159 | |
| 160 | **Near-clones:** Same `signature_id`, different `body_hash`. Same public contract (parameters + return type), diverged implementation — a maintainability risk. |
| 161 | |
| 162 | **Output:** Clusters, one per group, listing all member addresses. |
| 163 | |
| 164 | --- |
| 165 | |
| 166 | ### `muse checkout-symbol ADDRESS --commit REF` |
| 167 | |
| 168 | Restore a single named symbol from a historical commit into the current working tree. Only the target symbol's lines change; everything else is untouched. |
| 169 | |
| 170 | ``` |
| 171 | muse checkout-symbol src/billing.py::compute_total --commit v1.0 |
| 172 | muse checkout-symbol src/billing.py::compute_total --commit abc123 --dry-run |
| 173 | ``` |
| 174 | |
| 175 | **Flags:** |
| 176 | - `--commit REF` *(required)* — source commit |
| 177 | - `--dry-run` — print the unified diff without writing |
| 178 | |
| 179 | **Safety:** Rejects the operation if the target file cannot be parsed (syntax error) or if the symbol no longer exists at the destination location and the file cannot be safely patched. |
| 180 | |
| 181 | --- |
| 182 | |
| 183 | ### `muse semantic-cherry-pick ADDRESS... --from REF` |
| 184 | |
| 185 | Cherry-pick one or more named symbols from a historical commit. Applies each symbol patch to the working tree at the symbol's current location; appends at the end of the file if the symbol is not present in the current tree. |
| 186 | |
| 187 | ``` |
| 188 | muse semantic-cherry-pick src/billing.py::compute_total --from v1.0 |
| 189 | muse semantic-cherry-pick src/billing.py::f1 src/billing.py::f2 --from abc123 |
| 190 | muse semantic-cherry-pick src/billing.py::compute_total --from v1.0 --dry-run --json |
| 191 | ``` |
| 192 | |
| 193 | **Flags:** |
| 194 | - `--from REF` *(required)* — source commit |
| 195 | - `--dry-run` — show what would change without writing |
| 196 | - `--json` — structured output with per-symbol patch results |
| 197 | |
| 198 | --- |
| 199 | |
| 200 | ## 3. Query & Temporal Search |
| 201 | |
| 202 | ### `muse query PREDICATE...` |
| 203 | |
| 204 | Symbol graph predicate DSL — SQL for your codebase. |
| 205 | |
| 206 | ``` |
| 207 | muse query kind=function language=Python |
| 208 | muse query "(kind=function OR kind=method) name^=_" |
| 209 | muse query "NOT kind=import file~=billing" |
| 210 | muse query kind=function name~=validate --all-commits |
| 211 | muse query hash=a3f2c9 --all-commits --first |
| 212 | muse query --commit v1.0 kind=class |
| 213 | muse query kind=function --json |
| 214 | ``` |
| 215 | |
| 216 | #### Predicate Grammar (v2) |
| 217 | |
| 218 | ``` |
| 219 | expr = or_expr |
| 220 | or_expr = and_expr ( "OR" and_expr )* |
| 221 | and_expr = not_expr ( and_expr )* # implicit AND |
| 222 | not_expr = "NOT" primary | primary |
| 223 | primary = "(" expr ")" | atom |
| 224 | atom = KEY OP VALUE |
| 225 | ``` |
| 226 | |
| 227 | #### Operators |
| 228 | |
| 229 | | Operator | Meaning | |
| 230 | |---|---| |
| 231 | | `=` | Exact match (case-insensitive for strings) | |
| 232 | | `~=` | Contains substring | |
| 233 | | `^=` | Starts with | |
| 234 | | `$=` | Ends with | |
| 235 | | `!=` | Not equal | |
| 236 | | `>=` | Greater than or equal (lineno keys only) | |
| 237 | | `<=` | Less than or equal (lineno keys only) | |
| 238 | |
| 239 | #### Keys |
| 240 | |
| 241 | | Key | Type | Description | |
| 242 | |---|---|---| |
| 243 | | `kind` | string | `function`, `class`, `method`, `variable`, `import`, … | |
| 244 | | `language` | string | `Python`, `Go`, `Rust`, `TypeScript`, … | |
| 245 | | `name` | string | Bare symbol name | |
| 246 | | `qualified_name` | string | Dotted qualified name (e.g. `MyClass.save`) | |
| 247 | | `file` | string | File path (relative to repo root) | |
| 248 | | `hash` | string | `content_id` prefix (hex) | |
| 249 | | `body_hash` | string | `body_hash` prefix | |
| 250 | | `signature_id` | string | `signature_id` prefix | |
| 251 | | `lineno_gt` | integer | Symbol starts *after* this line number | |
| 252 | | `lineno_lt` | integer | Symbol starts *before* this line number | |
| 253 | |
| 254 | #### Flags |
| 255 | |
| 256 | | Flag | Description | |
| 257 | |---|---| |
| 258 | | `--commit REF` | Query a specific commit (mutually exclusive with `--all-commits`) | |
| 259 | | `--all-commits` | Walk all commits, deduplicate by `content_id`, annotate first-seen commit | |
| 260 | | `--first` | With `--all-commits`: keep only the first appearance of each unique body | |
| 261 | | `--json` | JSON output with `schema_version: 2` wrapper | |
| 262 | |
| 263 | --- |
| 264 | |
| 265 | ### `muse query-history PREDICATE... [--from REF] [--to REF]` |
| 266 | |
| 267 | Temporal symbol search — track matching symbols across a commit range. |
| 268 | |
| 269 | ``` |
| 270 | muse query-history kind=function language=Python |
| 271 | muse query-history name~=validate --from v1.0 --to HEAD |
| 272 | muse query-history kind=class --json |
| 273 | ``` |
| 274 | |
| 275 | **Output:** For each matching symbol address, reports `first_seen`, `last_seen`, `commit_count` (how many commits touched it), and `change_count` (how many times its `content_id` changed). |
| 276 | |
| 277 | **JSON schema:** |
| 278 | ```json |
| 279 | { |
| 280 | "schema_version": 2, |
| 281 | "query": "kind=function language=Python", |
| 282 | "from_ref": "v1.0", |
| 283 | "to_ref": "HEAD", |
| 284 | "results": [ |
| 285 | { |
| 286 | "address": "src/billing.py::compute_total", |
| 287 | "first_seen": "commit_id...", |
| 288 | "last_seen": "commit_id...", |
| 289 | "commit_count": 12, |
| 290 | "change_count": 3 |
| 291 | } |
| 292 | ] |
| 293 | } |
| 294 | ``` |
| 295 | |
| 296 | --- |
| 297 | |
| 298 | ## 4. Index Infrastructure |
| 299 | |
| 300 | ### `muse index status` |
| 301 | |
| 302 | Show present/absent/corrupt status and entry counts for all local indexes. |
| 303 | |
| 304 | ``` |
| 305 | muse index status |
| 306 | muse index status --json |
| 307 | ``` |
| 308 | |
| 309 | ### `muse index rebuild` |
| 310 | |
| 311 | Rebuild one or all indexes by walking the full commit history. |
| 312 | |
| 313 | ``` |
| 314 | muse index rebuild |
| 315 | muse index rebuild --index symbol_history |
| 316 | muse index rebuild --index hash_occurrence |
| 317 | ``` |
| 318 | |
| 319 | **Flags:** |
| 320 | - `--index NAME` — rebuild only this index (default: all) |
| 321 | |
| 322 | ### Index Design |
| 323 | |
| 324 | Indexes live under `.muse/indices/` and are: |
| 325 | - **Derived** — computed entirely from the commit history. |
| 326 | - **Optional** — no command requires them for correctness; they only provide speed. |
| 327 | - **Fully rebuildable** — `muse index rebuild` reconstructs them from scratch in one pass. |
| 328 | - **Versioned** — `schema_version` field for forward compatibility. |
| 329 | |
| 330 | #### `symbol_history` index |
| 331 | |
| 332 | Maps `symbol_address → list[HistoryEntry]` (chronological). Enables O(1) lineage lookups instead of O(commits × files) scans. |
| 333 | |
| 334 | #### `hash_occurrence` index |
| 335 | |
| 336 | Maps `body_hash → list[symbol_address]`. Enables O(1) clone detection and `muse find-symbol hash=` queries. |
| 337 | |
| 338 | --- |
| 339 | |
| 340 | ## 5. Symbol Identity Detail |
| 341 | |
| 342 | ### New `SymbolRecord` fields |
| 343 | |
| 344 | `SymbolRecord` gains two backward-compatible fields (empty string `""` for pre-v2 records): |
| 345 | |
| 346 | **`metadata_id`** |
| 347 | : SHA-256 of the symbol's *metadata wrapper* — decorators + async flag for Python functions, decorator list + base classes for Python classes. Allows distinguishing a decorator change from a body change. |
| 348 | |
| 349 | **`canonical_key`** |
| 350 | : `{file}#{scope}#{kind}#{name}#{lineno}` — a stable, unique machine handle for a symbol within a snapshot. Enables agent-to-agent symbol handoff without re-querying. Disambiguates overloaded names and nested scopes. |
| 351 | |
| 352 | ### `muse detect-refactor` (v2 output) |
| 353 | |
| 354 | With `--json`, emits `schema_version: 2` with a richer classification: |
| 355 | |
| 356 | ```json |
| 357 | { |
| 358 | "schema_version": 2, |
| 359 | "from_commit": "abc...", |
| 360 | "to_commit": "def...", |
| 361 | "total": 3, |
| 362 | "events": [ |
| 363 | { |
| 364 | "old_address": "src/billing.py::compute_total", |
| 365 | "new_address": "src/billing.py::compute_invoice_total", |
| 366 | "old_kind": "function", |
| 367 | "new_kind": "function", |
| 368 | "exact_classification": "rename", |
| 369 | "inferred_refactor": "none", |
| 370 | "confidence": 1.0, |
| 371 | "evidence": ["body_hash matches a1b2c3d4"], |
| 372 | "old_content_id": "ab12cd34", |
| 373 | "new_content_id": "ef56gh78", |
| 374 | "old_body_hash": "a1b2c3d4", |
| 375 | "new_body_hash": "a1b2c3d4" |
| 376 | } |
| 377 | ] |
| 378 | } |
| 379 | ``` |
| 380 | |
| 381 | **`exact_classification`** values: `rename`, `move`, `rename+move`, `signature_only`, `impl_only`, `metadata_only`, `full_rewrite`, `unchanged`. |
| 382 | |
| 383 | **`inferred_refactor`** values: `extract`, `inline`, `split`, `merge`, `none`. |
| 384 | |
| 385 | --- |
| 386 | |
| 387 | ## 6. Multi-Agent Coordination Layer |
| 388 | |
| 389 | The coordination layer enables thousands of agents to work on the same codebase simultaneously without stepping on each other. It is **purely advisory** — the VCS engine never reads coordination data for correctness decisions. Agents that ignore it still produce correct commits. |
| 390 | |
| 391 | ### Storage Layout |
| 392 | |
| 393 | ``` |
| 394 | .muse/coordination/ |
| 395 | reservations/<uuid>.json advisory symbol lease |
| 396 | intents/<uuid>.json declared operation before edit |
| 397 | ``` |
| 398 | |
| 399 | All records are **write-once** (never mutated) and use TTL-based expiry. Expired records are kept for audit purposes but ignored by all commands. |
| 400 | |
| 401 | --- |
| 402 | |
| 403 | ### `muse reserve ADDRESS... [OPTIONS]` |
| 404 | |
| 405 | Announce intent to edit one or more symbol addresses. |
| 406 | |
| 407 | ``` |
| 408 | muse reserve src/billing.py::compute_total |
| 409 | muse reserve src/billing.py::f1 src/billing.py::f2 --run-id agent-007 --ttl 7200 |
| 410 | muse reserve src/billing.py::compute_total --op rename |
| 411 | muse reserve src/billing.py::compute_total --json |
| 412 | ``` |
| 413 | |
| 414 | **Flags:** |
| 415 | - `--run-id ID` — identifier for this agent/run (default: random UUID) |
| 416 | - `--ttl SECONDS` — reservation expiry in seconds (default: 3600) |
| 417 | - `--op OPERATION` — declared operation: `rename`, `move`, `extract`, `modify`, `delete` |
| 418 | - `--json` — JSON output |
| 419 | |
| 420 | **Conflict detection:** Warns (but never blocks) if any of the requested addresses are already reserved by another active reservation. |
| 421 | |
| 422 | **Reservation schema (v1):** |
| 423 | ```json |
| 424 | { |
| 425 | "schema_version": 1, |
| 426 | "reservation_id": "<uuid>", |
| 427 | "run_id": "<agent-supplied ID>", |
| 428 | "branch": "<current branch>", |
| 429 | "addresses": ["src/billing.py::compute_total"], |
| 430 | "created_at": "2026-03-18T12:00:00+00:00", |
| 431 | "expires_at": "2026-03-18T13:00:00+00:00", |
| 432 | "operation": "rename" |
| 433 | } |
| 434 | ``` |
| 435 | |
| 436 | --- |
| 437 | |
| 438 | ### `muse intent ADDRESS... --op OPERATION [OPTIONS]` |
| 439 | |
| 440 | Declare a specific operation before executing it. More precise than a reservation; enables `muse forecast` to produce accurate conflict predictions. |
| 441 | |
| 442 | ``` |
| 443 | muse intent src/billing.py::compute_total --op rename --detail "rename to compute_invoice_total" |
| 444 | muse intent src/billing.py::compute_total --op modify --reservation-id <uuid> |
| 445 | ``` |
| 446 | |
| 447 | **Flags:** |
| 448 | - `--op OPERATION` *(required)* — `rename`, `move`, `extract`, `modify`, `delete`, `refactor` |
| 449 | - `--detail TEXT` — free-text description of the planned change |
| 450 | - `--reservation-id UUID` — link to an existing reservation |
| 451 | - `--run-id ID` — agent identifier |
| 452 | - `--json` — JSON output |
| 453 | |
| 454 | **Intent schema (v1):** |
| 455 | ```json |
| 456 | { |
| 457 | "schema_version": 1, |
| 458 | "intent_id": "<uuid>", |
| 459 | "reservation_id": "<uuid or empty>", |
| 460 | "run_id": "<agent ID>", |
| 461 | "branch": "<current branch>", |
| 462 | "addresses": ["src/billing.py::compute_total"], |
| 463 | "operation": "rename", |
| 464 | "created_at": "2026-03-18T12:00:00+00:00", |
| 465 | "detail": "rename to compute_invoice_total" |
| 466 | } |
| 467 | ``` |
| 468 | |
| 469 | --- |
| 470 | |
| 471 | ### `muse forecast [OPTIONS]` |
| 472 | |
| 473 | Predict merge conflicts from active reservations and intents — **before** writing any code. |
| 474 | |
| 475 | ``` |
| 476 | muse forecast |
| 477 | muse forecast --branch feature-x |
| 478 | muse forecast --json |
| 479 | ``` |
| 480 | |
| 481 | **Conflict types detected:** |
| 482 | |
| 483 | | Type | Confidence | Condition | |
| 484 | |---|---|---| |
| 485 | | `address_overlap` | 1.0 | Two reservations on the same symbol address | |
| 486 | | `blast_radius_overlap` | 0.75 | Reservations on symbols that call each other (via call graph) | |
| 487 | | `operation_conflict` | 0.9 | Two reservations declare incompatible operations (e.g. both `rename`) | |
| 488 | |
| 489 | **Flags:** |
| 490 | - `--branch BRANCH` — restrict to reservations on this branch |
| 491 | - `--json` — structured conflict list |
| 492 | |
| 493 | --- |
| 494 | |
| 495 | ### `muse plan-merge OURS THEIRS [OPTIONS]` |
| 496 | |
| 497 | Dry-run semantic merge plan — classify all symbol conflicts without writing anything. |
| 498 | |
| 499 | ``` |
| 500 | muse plan-merge main feature-x |
| 501 | muse plan-merge HEAD~5 HEAD --json |
| 502 | ``` |
| 503 | |
| 504 | **Output:** Classifies each diverging symbol into one of: |
| 505 | - `no_conflict` — diverged in disjoint symbols |
| 506 | - `symbol_edit_overlap` — both sides modified the same symbol |
| 507 | - `rename_edit` — one side renamed, the other modified |
| 508 | - `delete_use` — one side deleted a symbol still used by the other |
| 509 | |
| 510 | **Flags:** |
| 511 | - `--json` — structured output with full classification details |
| 512 | |
| 513 | --- |
| 514 | |
| 515 | ### `muse shard --agents N [OPTIONS]` |
| 516 | |
| 517 | Partition the codebase into N low-coupling work zones for parallel agent assignment. |
| 518 | |
| 519 | ``` |
| 520 | muse shard --agents 4 |
| 521 | muse shard --agents 8 --language Python |
| 522 | muse shard --agents 4 --json |
| 523 | ``` |
| 524 | |
| 525 | **Algorithm:** Builds the import graph, finds connected components, greedily merges small components into N balanced shards (by symbol count). Reports cross-shard edges as a coupling score (lower is better). |
| 526 | |
| 527 | **Flags:** |
| 528 | - `--agents N` *(required)* — number of shards |
| 529 | - `--language LANG` — restrict to files of this language |
| 530 | - `--json` — shard assignments as JSON |
| 531 | |
| 532 | --- |
| 533 | |
| 534 | ### `muse reconcile [OPTIONS]` |
| 535 | |
| 536 | Recommend merge ordering and integration strategy from the current coordination state. |
| 537 | |
| 538 | ``` |
| 539 | muse reconcile |
| 540 | muse reconcile --json |
| 541 | ``` |
| 542 | |
| 543 | **Output:** For each active branch with reservations, recommends: |
| 544 | - **Merge order** — branches with fewer predicted conflicts should merge first |
| 545 | - **Integration strategy** — `fast-forward`, `rebase`, or `manual` (when conflicts are predicted) |
| 546 | - **Conflict hotspots** — addresses that appear in the most reservations |
| 547 | |
| 548 | --- |
| 549 | |
| 550 | ## 7. Merge Engine & Architectural Enforcement |
| 551 | |
| 552 | ### `ConflictRecord` — Structured Conflict Taxonomy |
| 553 | |
| 554 | `MergeResult` now carries `conflict_records: list[ConflictRecord]` alongside the existing `conflicts: list[str]`. Each `ConflictRecord` provides structured metadata for programmatic conflict handling: |
| 555 | |
| 556 | ```python |
| 557 | @dataclass |
| 558 | class ConflictRecord: |
| 559 | path: str |
| 560 | conflict_type: str = "file_level" # see taxonomy below |
| 561 | ours_summary: str = "" |
| 562 | theirs_summary: str = "" |
| 563 | addresses: list[str] = field(default_factory=list) |
| 564 | ``` |
| 565 | |
| 566 | **`conflict_type` taxonomy:** |
| 567 | |
| 568 | | Value | Meaning | |
| 569 | |---|---| |
| 570 | | `symbol_edit_overlap` | Both branches modified the same symbol | |
| 571 | | `rename_edit` | One branch renamed, the other modified | |
| 572 | | `move_edit` | One branch moved, the other modified | |
| 573 | | `delete_use` | One branch deleted a symbol still used by the other | |
| 574 | | `dependency_conflict` | Conflicting changes to interdependent symbols | |
| 575 | | `file_level` | Legacy — no symbol-level information available | |
| 576 | |
| 577 | --- |
| 578 | |
| 579 | ### `muse breakage` |
| 580 | |
| 581 | Detect symbol-level structural breakage in the current working tree vs HEAD. |
| 582 | |
| 583 | ``` |
| 584 | muse breakage |
| 585 | muse breakage --language Python |
| 586 | muse breakage --json |
| 587 | ``` |
| 588 | |
| 589 | **Checks performed:** |
| 590 | |
| 591 | 1. **`stale_import`** — a `from X import Y` where `Y` no longer exists in the committed version of `X` (detected via symbol graph, not execution). |
| 592 | 2. **`missing_interface_method`** — a class body is missing a method that exists in the HEAD snapshot's version of that class. |
| 593 | |
| 594 | **What it does NOT do:** Execute code, install packages, run mypy or a type checker, or access the network. Pure structural analysis. |
| 595 | |
| 596 | **JSON output:** |
| 597 | ```json |
| 598 | { |
| 599 | "breakage_count": 2, |
| 600 | "issues": [ |
| 601 | { |
| 602 | "issue_type": "stale_import", |
| 603 | "file": "src/billing.py", |
| 604 | "description": "imports compute_total from src/utils.py but compute_total was removed" |
| 605 | } |
| 606 | ] |
| 607 | } |
| 608 | ``` |
| 609 | |
| 610 | --- |
| 611 | |
| 612 | ### `muse invariants` |
| 613 | |
| 614 | Enforce architectural rules declared in `.muse/invariants.toml`. |
| 615 | |
| 616 | ``` |
| 617 | muse invariants |
| 618 | muse invariants --commit HEAD~5 |
| 619 | muse invariants --json |
| 620 | ``` |
| 621 | |
| 622 | **Rule types:** |
| 623 | |
| 624 | #### `no_cycles` |
| 625 | ```toml |
| 626 | [[rules]] |
| 627 | type = "no_cycles" |
| 628 | name = "no import cycles" |
| 629 | ``` |
| 630 | The import graph must be a DAG. Reports every cycle as a violation. |
| 631 | |
| 632 | #### `forbidden_dependency` |
| 633 | ```toml |
| 634 | [[rules]] |
| 635 | type = "forbidden_dependency" |
| 636 | name = "core must not import cli" |
| 637 | source_pattern = "muse/core/" |
| 638 | forbidden_pattern = "muse/cli/" |
| 639 | ``` |
| 640 | Files matching `source_pattern` must not import from files matching `forbidden_pattern`. |
| 641 | |
| 642 | #### `layer_boundary` |
| 643 | ```toml |
| 644 | [[rules]] |
| 645 | type = "layer_boundary" |
| 646 | name = "plugins must not import from cli" |
| 647 | lower = "muse/plugins/" |
| 648 | upper = "muse/cli/" |
| 649 | ``` |
| 650 | Files in `lower` must not import from files in `upper` (enforces layered architecture). |
| 651 | |
| 652 | #### `required_test` |
| 653 | ```toml |
| 654 | [[rules]] |
| 655 | type = "required_test" |
| 656 | name = "all billing functions must have tests" |
| 657 | source_pattern = "src/billing.py" |
| 658 | test_pattern = "tests/test_billing.py" |
| 659 | ``` |
| 660 | Every public function in `source_pattern` must have a corresponding test function in `test_pattern` (matched by bare name). |
| 661 | |
| 662 | **Bootstrapping:** If `.muse/invariants.toml` does not exist, `muse invariants` creates it with a commented template and exits with a guided onboarding message. |
| 663 | |
| 664 | --- |
| 665 | |
| 666 | ## 8. Semantic Versioning |
| 667 | |
| 668 | Muse automatically assigns semantic version bumps at commit time based on the `StructuredDelta`. |
| 669 | |
| 670 | ### `SemVerBump` |
| 671 | |
| 672 | ```python |
| 673 | SemVerBump = Literal["major", "minor", "patch", "none"] |
| 674 | ``` |
| 675 | |
| 676 | ### Inference rules (`infer_sem_ver_bump`) |
| 677 | |
| 678 | | Change type | Bump | Breaking? | |
| 679 | |---|---|---| |
| 680 | | Delete a public symbol | `major` | yes — address added to `breaking_changes` | |
| 681 | | Rename a public symbol | `major` | yes — old address added to `breaking_changes` | |
| 682 | | `signature_only` change | `major` | yes — callers may break | |
| 683 | | Insert a new public symbol | `minor` | no | |
| 684 | | `impl_only` change (body only) | `patch` | no | |
| 685 | | `metadata_only` change | `none` | no | |
| 686 | | Formatting-only change | `none` | no | |
| 687 | | Non-public symbol changes | `patch` or `none` | no | |
| 688 | |
| 689 | **Public** = name does not start with `_` and kind is `function`, `class`, `method`, or `async_function`. |
| 690 | |
| 691 | ### Storage |
| 692 | |
| 693 | Both `StructuredDelta` and `CommitRecord` carry: |
| 694 | - `sem_ver_bump: SemVerBump` (default `"none"`) |
| 695 | - `breaking_changes: list[str]` (default `[]`) |
| 696 | |
| 697 | These fields are backward-compatible — pre-v2 commits read as `"none"` / `[]`. |
| 698 | |
| 699 | ### `muse log` display |
| 700 | |
| 701 | When a commit's `sem_ver_bump` is non-`none`, long-form `muse log` output appends: |
| 702 | ``` |
| 703 | SemVer: MAJOR |
| 704 | Breaking: src/billing.py::compute_total, src/billing.py::Invoice (+2 more) |
| 705 | ``` |
| 706 | |
| 707 | --- |
| 708 | |
| 709 | ## 9. Call-Graph Tier Commands |
| 710 | |
| 711 | ### `muse impact ADDRESS [OPTIONS]` |
| 712 | |
| 713 | Transitive blast-radius analysis — what else breaks if this function changes? |
| 714 | |
| 715 | ``` |
| 716 | muse impact src/billing.py::compute_total |
| 717 | muse impact src/billing.py::compute_total --commit HEAD~5 |
| 718 | muse impact src/billing.py::compute_total --json |
| 719 | ``` |
| 720 | |
| 721 | **Algorithm:** BFS over the reverse call graph (Python only via `ast`). Traverses until the transitive closure is exhausted, annotating each affected symbol with its depth. |
| 722 | |
| 723 | **Risk levels:** 🟢 (0–2 callers), 🟡 (3–9 callers), 🔴 (10+ callers). |
| 724 | |
| 725 | --- |
| 726 | |
| 727 | ### `muse dead [OPTIONS]` |
| 728 | |
| 729 | Dead code detection — symbols with no callers and no importers. |
| 730 | |
| 731 | ``` |
| 732 | muse dead |
| 733 | muse dead --kind function |
| 734 | muse dead --exclude-tests |
| 735 | muse dead --json |
| 736 | ``` |
| 737 | |
| 738 | **Detection logic:** A symbol is a dead-code candidate when: |
| 739 | 1. Its bare name appears in no `ast.Call` node in the snapshot **and** |
| 740 | 2. Its module is not imported anywhere in the snapshot. |
| 741 | |
| 742 | **Distinction:** `definite_dead` (module never imported) vs `soft_dead` (module imported but function never called directly). |
| 743 | |
| 744 | --- |
| 745 | |
| 746 | ### `muse coverage CLASS_ADDRESS [OPTIONS]` |
| 747 | |
| 748 | Class interface call-coverage — which methods of a class are actually called? |
| 749 | |
| 750 | ``` |
| 751 | muse coverage src/billing.py::Invoice |
| 752 | muse coverage src/billing.py::Invoice --show-callers |
| 753 | muse coverage src/billing.py::Invoice --json |
| 754 | ``` |
| 755 | |
| 756 | **Output:** Lists every method of the class, marks which ones appear in `ast.Call` nodes anywhere in the snapshot, and prints a coverage percentage. No test suite required. |
| 757 | |
| 758 | --- |
| 759 | |
| 760 | ### `muse deps ADDRESS_OR_FILE [OPTIONS]` |
| 761 | |
| 762 | Import graph + call-graph analysis. |
| 763 | |
| 764 | ``` |
| 765 | muse deps src/billing.py |
| 766 | muse deps src/billing.py --reverse |
| 767 | muse deps src/billing.py::compute_total |
| 768 | muse deps src/billing.py::compute_total --reverse |
| 769 | muse deps src/billing.py --commit v1.0 --json |
| 770 | ``` |
| 771 | |
| 772 | **File mode:** Lists all `import`-kind symbols from the file (what does it import?). With `--reverse`: which other files import this one. |
| 773 | |
| 774 | **Symbol mode** (`address` contains `::`): Python-only call extraction — which functions does this function call? With `--reverse`: which functions call this one. |
| 775 | |
| 776 | --- |
| 777 | |
| 778 | ### `muse find-symbol [OPTIONS]` |
| 779 | |
| 780 | Cross-commit, cross-branch symbol search by hash, name, or kind. |
| 781 | |
| 782 | ``` |
| 783 | muse find-symbol --hash a3f2c9 |
| 784 | muse find-symbol --name compute_total |
| 785 | muse find-symbol --name compute_* --kind function |
| 786 | muse find-symbol --hash a3f2c9 --all-branches --first |
| 787 | muse find-symbol --name validate --json |
| 788 | ``` |
| 789 | |
| 790 | **Flags:** |
| 791 | - `--hash HEX` — match `content_id` prefix (exact body match across history) |
| 792 | - `--name NAME` — exact name or prefix glob with `*` |
| 793 | - `--kind KIND` — restrict to symbol kind |
| 794 | - `--all-branches` — also scan all branch tips in `.muse/refs/heads/` |
| 795 | - `--first` — deduplicate on `content_id`, keeping only the first appearance |
| 796 | - `--json` — structured output |
| 797 | |
| 798 | --- |
| 799 | |
| 800 | ### `muse patch ADDRESS SOURCE [OPTIONS]` |
| 801 | |
| 802 | Surgical semantic patch — replace exactly one named symbol in the working tree. |
| 803 | |
| 804 | ``` |
| 805 | muse patch src/billing.py::compute_total new_impl.py |
| 806 | echo "def compute_total(x): return x * 2" | muse patch src/billing.py::compute_total - |
| 807 | muse patch src/billing.py::compute_total new_impl.py --dry-run |
| 808 | ``` |
| 809 | |
| 810 | **Syntax validation:** Before writing, validates the replacement source with: |
| 811 | - `ast.parse` for Python |
| 812 | - `tree-sitter` CST error-node check for all 11 supported languages |
| 813 | |
| 814 | Rejects the patch and exits non-zero if the source has syntax errors. |
| 815 | |
| 816 | **Flags:** |
| 817 | - `--dry-run` — print the unified diff without writing |
| 818 | - `--json` — structured output with patch result |
| 819 | |
| 820 | --- |
| 821 | |
| 822 | ## 10. Architecture Internals |
| 823 | |
| 824 | ### Module Map |
| 825 | |
| 826 | ``` |
| 827 | muse/ |
| 828 | plugins/code/ |
| 829 | plugin.py MidiPlugin → CodePlugin (MuseDomainPlugin + StructuredMergePlugin) |
| 830 | ast_parser.py Python AST → SymbolRecord; validate_syntax() for all 11 languages |
| 831 | symbol_diff.py diff_symbol_trees() — O(n) diffing, rename/move annotation |
| 832 | _query.py symbols_for_snapshot(), walk_commits(), language_of() |
| 833 | _predicate.py Predicate DSL parser — tokenise → recursive descent → Predicate callable |
| 834 | _callgraph.py ForwardGraph, ReverseGraph, build_*, transitive_callers BFS |
| 835 | _refactor_classify.py classify_exact(), classify_composite(), RefactorClassification |
| 836 | core/ |
| 837 | coordination.py Reservation, Intent, create/load helpers, .muse/coordination/ |
| 838 | indices.py SymbolHistoryIndex, HashOccurrenceIndex, save/load/rebuild |
| 839 | ``` |
| 840 | |
| 841 | ### Language Support |
| 842 | |
| 843 | | Language | Extension(s) | Parser | Symbol types | |
| 844 | |---|---|---|---| |
| 845 | | Python | `.py` | `ast` (stdlib) | function, async_function, class, method, variable, import | |
| 846 | | JavaScript | `.js` `.jsx` `.mjs` `.cjs` | tree-sitter | function, class, method | |
| 847 | | TypeScript | `.ts` `.tsx` | tree-sitter | function, class, method, interface, type_alias, enum | |
| 848 | | Go | `.go` | tree-sitter | function (method qualified as `Type.Method`) | |
| 849 | | Rust | `.rs` | tree-sitter | function (impl method qualified as `Type.method`) | |
| 850 | | Java | `.java` | tree-sitter | class, interface, method, constructor, enum | |
| 851 | | C | `.c` `.h` | tree-sitter | function_definition | |
| 852 | | C++ | `.cpp` `.cc` `.cxx` `.hpp` | tree-sitter | function, class, struct | |
| 853 | | C# | `.cs` | tree-sitter | class, interface, struct, method, constructor, enum | |
| 854 | | Ruby | `.rb` | tree-sitter | class, module, method, singleton_method | |
| 855 | | Kotlin | `.kt` `.kts` | tree-sitter | function, class, method | |
| 856 | |
| 857 | ### Layer Rules |
| 858 | |
| 859 | - `muse/core/*` is domain-agnostic — never imports from `muse/plugins/*` |
| 860 | - `muse/cli/commands/*` are thin — delegate all logic to `muse/core/*` or plugin helpers |
| 861 | - `muse/plugins/code/*` is the only layer that imports domain-specific AST logic |
| 862 | - `muse/core/coordination.py` and `muse/core/indices.py` are domain-agnostic helpers |
| 863 | |
| 864 | --- |
| 865 | |
| 866 | ## 11. Type Reference |
| 867 | |
| 868 | ### `SymbolRecord` (TypedDict) |
| 869 | |
| 870 | ```python |
| 871 | class SymbolRecord(TypedDict): |
| 872 | kind: str # function | class | method | variable | import | … |
| 873 | name: str # bare name |
| 874 | qualified_name: str # dotted path (e.g. MyClass.save) |
| 875 | lineno: int |
| 876 | end_lineno: int |
| 877 | content_id: str # SHA-256 of full normalized AST |
| 878 | body_hash: str # SHA-256 of body only |
| 879 | signature_id: str # SHA-256 of signature only |
| 880 | metadata_id: str # SHA-256 of decorators + async + bases (v2, "" for pre-v2) |
| 881 | canonical_key: str # {file}#{scope}#{kind}#{name}#{lineno} (v2, "" for pre-v2) |
| 882 | ``` |
| 883 | |
| 884 | ### `StructuredDelta` |
| 885 | |
| 886 | ```python |
| 887 | class StructuredDelta(TypedDict): |
| 888 | domain: str |
| 889 | ops: list[DomainOp] |
| 890 | summary: str |
| 891 | sem_ver_bump: SemVerBump # default "none" |
| 892 | breaking_changes: list[str] # default [] |
| 893 | ``` |
| 894 | |
| 895 | ### `DomainOp` union |
| 896 | |
| 897 | ```python |
| 898 | DomainOp = InsertOp | DeleteOp | ReplaceOp | MoveOp | PatchOp |
| 899 | ``` |
| 900 | |
| 901 | Each op is a `TypedDict` discriminated by a `Literal` `"op"` field. |
| 902 | |
| 903 | ### `ConflictRecord` (dataclass) |
| 904 | |
| 905 | ```python |
| 906 | @dataclass |
| 907 | class ConflictRecord: |
| 908 | path: str |
| 909 | conflict_type: str = "file_level" |
| 910 | ours_summary: str = "" |
| 911 | theirs_summary: str = "" |
| 912 | addresses: list[str] = field(default_factory=list) |
| 913 | ``` |
| 914 | |
| 915 | ### `Reservation` |
| 916 | |
| 917 | ```python |
| 918 | class Reservation: |
| 919 | reservation_id: str |
| 920 | run_id: str |
| 921 | branch: str |
| 922 | addresses: list[str] |
| 923 | created_at: datetime |
| 924 | expires_at: datetime |
| 925 | operation: str | None |
| 926 | def is_active(self) -> bool: ... |
| 927 | def to_dict(self) -> dict[str, str | int | list[str] | None]: ... |
| 928 | @classmethod |
| 929 | def from_dict(cls, d) -> Reservation: ... |
| 930 | ``` |
| 931 | |
| 932 | ### `Intent` |
| 933 | |
| 934 | ```python |
| 935 | class Intent: |
| 936 | intent_id: str |
| 937 | reservation_id: str |
| 938 | run_id: str |
| 939 | branch: str |
| 940 | addresses: list[str] |
| 941 | operation: str |
| 942 | created_at: datetime |
| 943 | detail: str |
| 944 | def to_dict(self) -> dict[str, str | int | list[str]]: ... |
| 945 | @classmethod |
| 946 | def from_dict(cls, d) -> Intent: ... |
| 947 | ``` |
| 948 | |
| 949 | ### `SemVerBump` |
| 950 | |
| 951 | ```python |
| 952 | SemVerBump = Literal["major", "minor", "patch", "none"] |
| 953 | ``` |
| 954 | |
| 955 | ### `Predicate` |
| 956 | |
| 957 | ```python |
| 958 | Predicate = Callable[[str, SymbolRecord], bool] |
| 959 | # first arg: file_path |
| 960 | # second arg: SymbolRecord |
| 961 | # returns: True if the symbol matches the predicate |
| 962 | ``` |
| 963 | |
| 964 | ### `ExactClassification` |
| 965 | |
| 966 | ```python |
| 967 | ExactClassification = Literal[ |
| 968 | "rename", "move", "rename+move", |
| 969 | "signature_only", "impl_only", "metadata_only", |
| 970 | "full_rewrite", "unchanged", |
| 971 | ] |
| 972 | ``` |
| 973 | |
| 974 | ### `InferredRefactor` |
| 975 | |
| 976 | ```python |
| 977 | InferredRefactor = Literal["extract", "inline", "split", "merge", "none"] |
| 978 | ``` |
| 979 | |
| 980 | --- |
| 981 | |
| 982 | ## Further Reading |
| 983 | |
| 984 | - [Plugin Authoring Guide](plugin-authoring-guide.md) — implementing `MuseDomainPlugin` |
| 985 | - [Type Contracts](type-contracts.md) — strict typing rules and enforcement |
| 986 | - [CRDT Reference](crdt-reference.md) — CRDT and OT merge primitives |
| 987 | - [Tour de Force — Code](../demo/tour-de-force-code.md) — full narrative walkthrough of all code commands |
| 988 | - [Tour de Force — Music](../demo/tour-de-force-music.md) — MIDI domain reference demo |