gabriel / muse public
code-domain.md markdown
1281 lines 40.2 KB
87c1ef22 docs: inject explicit HTML anchors in code-domain.md section headers Gabriel Cardona <gabriel@tellurstori.com> 17h ago
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. [Selective Staging (`muse code add`)](#1-selective-staging-muse-code-add)
25 2. [Symbol Identity Model](#2-symbol-identity-model)
26 3. [Provenance & Topology Commands](#3-provenance--topology-commands)
27 4. [Query & Temporal Search](#4-query--temporal-search)
28 5. [Index Infrastructure](#5-index-infrastructure)
29 6. [Symbol Identity Detail](#6-symbol-identity-detail)
30 7. [Multi-Agent Coordination Layer](#7-multi-agent-coordination-layer)
31 8. [Merge Engine & Architectural Enforcement](#8-merge-engine--architectural-enforcement)
32 9. [Semantic Versioning](#9-semantic-versioning)
33 10. [Call-Graph Tier Commands](#10-call-graph-tier-commands)
34 11. [Architecture Internals](#11-architecture-internals)
35 12. [Type Reference](#12-type-reference)
36
37 ---
38
39 <a id="1-selective-staging"></a>
40 ## 1. Selective Staging (`muse code add`)
41
42 The code domain adds a **Git-style staging index** to Muse. By default,
43 `muse commit` snapshots the entire working tree. Once you run `muse code add`,
44 the next commit includes *only* what you have explicitly staged — everything
45 else carries forward from the previous commit unchanged.
46
47 This lets you commit a coherent, working subset of your in-progress changes
48 without committing half-finished code.
49
50 ### Stage index location
51
52 `.muse/code/stage.json` — a JSON file that maps workspace-relative paths to
53 their staged object IDs and mode (`A` added / `M` modified / `D` deleted`).
54
55 ### `muse code add`
56
57 ```
58 muse code add <file> [<file> …] # stage one or more files
59 muse code add <dir> # stage every file under a directory
60 muse code add . # stage everything in the working tree
61 muse code add -A / --all # stage all changes, including new files
62 muse code add -u / --update # stage only tracked (already-committed) files
63 # modified or deleted on disk — no new files
64 muse code add -p / --patch <file> # interactive hunk-by-hunk staging
65 muse code add -n / --dry-run … # show what would be staged without staging
66 muse code add -v / --verbose … # print each file as it is staged
67 ```
68
69 #### Patch mode (`-p`)
70
71 Interactive hunk-by-hunk staging, mirroring `git add -p`. For each diff
72 hunk you are prompted:
73
74 | Key | Action |
75 |-----|--------|
76 | `y` | Stage this hunk |
77 | `n` | Skip this hunk |
78 | `q` | Quit; commit hunks accepted so far |
79 | `a` | Stage this and all remaining hunks in this file |
80 | `d` | Skip the rest of this file |
81 | `?` | Show help |
82
83 The partial file (accepted hunks only) is hashed, written to the object store,
84 and recorded in the stage index. The working tree is **never modified**.
85
86 Agents should use explicit file paths (`muse code add <file>`) and avoid
87 `--patch`, which requires an interactive terminal.
88
89 ### `muse code reset`
90
91 ```
92 muse code reset # unstage everything
93 muse code reset <file> # unstage a specific file
94 muse code reset HEAD <file> # same — mirrors Git syntax
95 ```
96
97 Removes files from the stage index without touching the working tree. The
98 working tree copy is always preserved.
99
100 ### `muse commit` with an active stage
101
102 When `.muse/code/stage.json` exists and is non-empty:
103
104 - Staged files → committed at their **staged** object ID.
105 - Tracked-but-unstaged files → carried forward at their **committed** (HEAD) object ID.
106 - Untracked files → not included in the commit.
107
108 After a successful commit the stage index is **cleared automatically**.
109
110 ### `muse status` with an active stage
111
112 When a stage is active, `muse status` renders a three-bucket view:
113
114 ```
115 On branch main
116
117 Changes staged for commit:
118 (use "muse code reset HEAD <file>" to unstage)
119
120 new file: src/auth.py
121 modified: src/models.py
122
123 Changes not staged for commit:
124 (use "muse code add <file>" to update what will be committed)
125
126 modified: src/broken_wip.py
127
128 Untracked files:
129 (use "muse code add <file>" to include in what will be committed)
130
131 tmp_experiment.py
132 ```
133
134 With `--format json`:
135
136 ```json
137 {
138 "branch": "main",
139 "clean": false,
140 "staged": {
141 "src/auth.py": {"mode": "A", "object_id": "<sha256>"}
142 },
143 "unstaged": {
144 "src/broken_wip.py": "modified"
145 },
146 "untracked": ["tmp_experiment.py"]
147 }
148 ```
149
150 ### Workflow example
151
152 ```bash
153 # Edit freely — nothing is committed until you stage.
154 vim src/auth.py src/models.py src/wip.py
155
156 # Stage only the production-ready files.
157 muse code add src/auth.py src/models.py
158
159 # Verify what will be committed.
160 muse status
161
162 # Commit exactly what was staged.
163 muse commit -m "feat: add auth + models"
164
165 # The working-tree copy of wip.py is untouched.
166 ```
167
168 ---
169
170 <a id="2-symbol-identity-model"></a>
171 ## 2. Symbol Identity Model
172
173 Every symbol carries four content-addressed hashes and two stable keys:
174
175 | Field | Description |
176 |---|---|
177 | `content_id` | SHA-256 of the full normalized AST (signature + body + metadata). Two symbols are identical iff their `content_id` matches. |
178 | `body_hash` | SHA-256 of the function/class body only (excluding signature and decorators). Matches across renames and decorator changes. |
179 | `signature_id` | SHA-256 of the normalized parameter list and return annotation. Matches across implementation-only changes. |
180 | `metadata_id` *(v2)* | SHA-256 of decorator list + async flag + base classes. Matches when only the implementation or signature changed. |
181 | `canonical_key` *(v2)* | `{file}#{scope}#{kind}#{name}#{lineno}` — stable machine handle for agent-to-agent symbol handoff. |
182 | `qualified_name` | Dotted path within the file (e.g. `MyClass.my_method`). |
183
184 ### Exact Refactor Classification
185
186 Two symbols are classified by comparing their four hashes:
187
188 | Classification | Condition |
189 |---|---|
190 | `unchanged` | `content_id` matches |
191 | `rename` | `body_hash` matches, name differs, same file |
192 | `move` | `content_id` matches, different file, same name |
193 | `rename+move` | `body_hash` matches, different file, different name |
194 | `signature_only` | `body_hash` matches, `signature_id` differs |
195 | `impl_only` | `signature_id` matches, `body_hash` differs |
196 | `metadata_only` | `body_hash` + `signature_id` match, `metadata_id` differs |
197 | `full_rewrite` | Both signature and body changed |
198
199 ---
200
201 <a id="3-provenance--topology-commands"></a>
202 ## 3. Provenance & Topology Commands
203
204 ### `muse code lineage ADDRESS`
205
206 Full provenance chain of a named symbol from its first appearance to the present.
207
208 ```
209 muse code lineage src/billing.py::compute_total
210 muse code lineage src/billing.py::compute_total --commit HEAD~10
211 muse code lineage src/billing.py::compute_total --json
212 ```
213
214 **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.
215
216 **Output events:** `created`, `modified`, `renamed_from`, `moved_from`, `deleted`.
217
218 **Flags:**
219 - `--commit REF` — stop history walk at this commit (default: HEAD)
220 - `--json` — emit a JSON array of event objects
221
222 **JSON schema:**
223 ```json
224 [
225 {
226 "event": "created",
227 "commit_id": "a1b2c3d4...",
228 "committed_at": "2026-01-01T00:00:00+00:00",
229 "message": "Initial commit",
230 "address": "src/billing.py::compute_total",
231 "content_id": "sha256..."
232 }
233 ]
234 ```
235
236 ---
237
238 ### `muse code api-surface`
239
240 Public API surface of a snapshot — every non-underscore function, class, and method.
241
242 ```
243 muse code api-surface
244 muse code api-surface --commit v1.0
245 muse code api-surface --diff v1.0
246 muse code api-surface --json
247 ```
248
249 **With `--diff REF`:** Shows three sections — **Added** (new public symbols), **Removed** (deleted public symbols), **Changed** (same address, different `content_id`).
250
251 **Public** means: `kind` in `{function, class, method, async_function}` and `name` not starting with `_`.
252
253 ---
254
255 ### `muse code codemap`
256
257 Semantic topology of the entire codebase at a snapshot.
258
259 ```
260 muse code codemap
261 muse code codemap --top 10
262 muse code codemap --commit HEAD~5
263 muse code codemap --json
264 ```
265
266 **What it shows:**
267 - **Modules by size** — ranked by symbol count
268 - **Import graph** — in-degree (how many modules import this one)
269 - **Cycles** — import cycles detected via DFS (a hard architectural smell)
270 - **High-centrality symbols** — functions called from many places (blast-radius risk)
271 - **Boundary files** — high fan-out (imports many), zero fan-in (nothing imports them)
272
273 **Flags:**
274 - `--top N` — show top N entries per section (default: 5)
275 - `--commit REF` — snapshot to analyse
276 - `--json` — structured output
277
278 ---
279
280 ### `muse code clones`
281
282 Find exact and near-duplicate symbol clusters across the snapshot.
283
284 ```
285 muse code clones
286 muse code clones --tier exact
287 muse code clones --tier near
288 muse code clones --tier both
289 muse code clones --commit HEAD~3 --json
290 ```
291
292 **Exact clones:** Same `body_hash` at different addresses. These are literal copy-paste duplicates — same implementation, possibly different name.
293
294 **Near-clones:** Same `signature_id`, different `body_hash`. Same public contract (parameters + return type), diverged implementation — a maintainability risk.
295
296 **Output:** Clusters, one per group, listing all member addresses.
297
298 ---
299
300 ### `muse code checkout-symbol ADDRESS --commit REF`
301
302 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.
303
304 ```
305 muse code checkout-symbol src/billing.py::compute_total --commit v1.0
306 muse code checkout-symbol src/billing.py::compute_total --commit abc123 --dry-run
307 muse code checkout-symbol src/billing.py::compute_total --commit v1.0 --json
308 ```
309
310 **Flags:**
311 - `--commit REF` *(required)* — source commit
312 - `--dry-run` — print the unified diff without writing
313 - `--json` — emit result as JSON for agent consumption
314
315 **JSON output:**
316
317 ```json
318 {
319 "address": "src/billing.py::compute_total",
320 "file": "src/billing.py",
321 "restored_from": "abc12345",
322 "dry_run": false
323 }
324 ```
325
326 **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.
327
328 **Security:** The file path component of ADDRESS is validated via `contain_path()` before any disk access. Paths that escape the repo root (e.g. `../../etc/passwd::foo`) are rejected with exit 1.
329
330 ---
331
332 ### `muse code semantic-cherry-pick ADDRESS... --from REF`
333
334 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.
335
336 ```
337 muse code semantic-cherry-pick src/billing.py::compute_total --from v1.0
338 muse code semantic-cherry-pick src/billing.py::f1 src/billing.py::f2 --from abc123
339 muse code semantic-cherry-pick src/billing.py::compute_total --from v1.0 --dry-run --json
340 ```
341
342 **Flags:**
343 - `--from REF` *(required)* — source commit
344 - `--dry-run` — show what would change without writing
345 - `--json` — structured output with per-symbol patch results
346
347 **JSON output:**
348
349 ```json
350 {
351 "from_commit": "abc12345",
352 "dry_run": false,
353 "results": [
354 {"address": "src/billing.py::compute_total", "status": "applied",
355 "detail": "lines 10–25 → 12 lines", "old_lines": 16, "new_lines": 12}
356 ],
357 "applied": 1,
358 "failed": 0,
359 "already_current": 0
360 }
361 ```
362
363 **Security:** Every file path extracted from ADDRESS arguments is validated via `contain_path()` before any disk I/O or directory creation. Paths that escape the repo root are recorded as `not_found` and the remaining symbols continue to be processed.
364
365 ---
366
367 <a id="4-query--temporal-search"></a>
368 ## 4. Query & Temporal Search
369
370 ### `muse code query PREDICATE...`
371
372 Symbol graph predicate DSL — SQL for your codebase.
373
374 ```
375 muse code query kind=function language=Python
376 muse code query "(kind=function OR kind=method) name^=_"
377 muse code query "NOT kind=import file~=billing"
378 muse code query kind=function name~=validate --all-commits
379 muse code query hash=a3f2c9 --all-commits --first
380 muse code query --commit v1.0 kind=class
381 muse code query kind=function --json
382 ```
383
384 #### Predicate Grammar (v2)
385
386 ```
387 expr = or_expr
388 or_expr = and_expr ( "OR" and_expr )*
389 and_expr = not_expr ( and_expr )* # implicit AND
390 not_expr = "NOT" primary | primary
391 primary = "(" expr ")" | atom
392 atom = KEY OP VALUE
393 ```
394
395 #### Operators
396
397 | Operator | Meaning |
398 |---|---|
399 | `=` | Exact match (case-insensitive for strings) |
400 | `~=` | Contains substring |
401 | `^=` | Starts with |
402 | `$=` | Ends with |
403 | `!=` | Not equal |
404 | `>=` | Greater than or equal (lineno keys only) |
405 | `<=` | Less than or equal (lineno keys only) |
406
407 #### Keys
408
409 | Key | Type | Description |
410 |---|---|---|
411 | `kind` | string | `function`, `class`, `method`, `variable`, `import`, … |
412 | `language` | string | `Python`, `Go`, `Rust`, `TypeScript`, … |
413 | `name` | string | Bare symbol name |
414 | `qualified_name` | string | Dotted qualified name (e.g. `MyClass.save`) |
415 | `file` | string | File path (relative to repo root) |
416 | `hash` | string | `content_id` prefix (hex) |
417 | `body_hash` | string | `body_hash` prefix |
418 | `signature_id` | string | `signature_id` prefix |
419 | `lineno_gt` | integer | Symbol starts *after* this line number |
420 | `lineno_lt` | integer | Symbol starts *before* this line number |
421
422 #### Flags
423
424 | Flag | Description |
425 |---|---|
426 | `--commit REF` | Query a specific commit (mutually exclusive with `--all-commits`) |
427 | `--all-commits` | Walk all commits, deduplicate by `content_id`, annotate first-seen commit |
428 | `--first` | With `--all-commits`: keep only the first appearance of each unique body |
429 | `--json` | JSON output with `schema_version: 2` wrapper |
430
431 ---
432
433 ### `muse code query-history PREDICATE... [--from REF] [--to REF]`
434
435 Temporal symbol search — track matching symbols across a commit range.
436
437 ```
438 muse code query-history kind=function language=Python
439 muse code query-history name~=validate --from v1.0 --to HEAD
440 muse code query-history kind=class --json
441 ```
442
443 **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).
444
445 **JSON schema:**
446 ```json
447 {
448 "schema_version": 2,
449 "query": "kind=function language=Python",
450 "from_ref": "v1.0",
451 "to_ref": "HEAD",
452 "results": [
453 {
454 "address": "src/billing.py::compute_total",
455 "first_seen": "commit_id...",
456 "last_seen": "commit_id...",
457 "commit_count": 12,
458 "change_count": 3
459 }
460 ]
461 }
462 ```
463
464 ---
465
466 <a id="5-index-infrastructure"></a>
467 ## 5. Index Infrastructure
468
469 ### `muse code index status`
470
471 Show present/absent/corrupt status and entry counts for all local indexes.
472
473 ```
474 muse code index status
475 muse code index status --json
476 ```
477
478 **Flags:**
479 - `--json` — emit status array as JSON
480
481 **JSON output:**
482
483 ```json
484 [
485 {"name": "symbol_history", "status": "present", "entries": 1024, "updated_at": "2026-03-21T12:00:00"},
486 {"name": "hash_occurrence", "status": "absent", "entries": 0, "updated_at": null}
487 ]
488 ```
489
490 ### `muse code index rebuild`
491
492 Rebuild one or all indexes by walking the full commit history.
493
494 ```
495 muse code index rebuild
496 muse code index rebuild --json
497 muse code index rebuild --index symbol_history
498 muse code index rebuild --index hash_occurrence --verbose
499 ```
500
501 **Flags:**
502 - `--index NAME` — rebuild only this index (default: all)
503 - `--verbose, -v` — show progress while building
504 - `--json` — emit rebuild summary as JSON
505
506 **JSON output:**
507
508 ```json
509 {
510 "rebuilt": ["symbol_history", "hash_occurrence"],
511 "symbol_history_addresses": 512,
512 "symbol_history_events": 2048,
513 "hash_occurrence_clusters": 31,
514 "hash_occurrence_addresses": 87
515 }
516 ```
517
518 ### Index Design
519
520 Indexes live under `.muse/indices/` and are:
521 - **Derived** — computed entirely from the commit history.
522 - **Optional** — no command requires them for correctness; they only provide speed.
523 - **Fully rebuildable** — `muse code index rebuild` reconstructs them from scratch in one pass.
524 - **Versioned** — `schema_version` field for forward compatibility.
525
526 #### `symbol_history` index
527
528 Maps `symbol_address → list[HistoryEntry]` (chronological). Enables O(1) lineage lookups instead of O(commits × files) scans.
529
530 #### `hash_occurrence` index
531
532 Maps `body_hash → list[symbol_address]`. Enables O(1) clone detection and `muse code find-symbol hash=` queries.
533
534 ---
535
536 <a id="6-symbol-identity-detail"></a>
537 ## 6. Symbol Identity Detail
538
539 ### New `SymbolRecord` fields
540
541 `SymbolRecord` gains two backward-compatible fields (empty string `""` for pre-v2 records):
542
543 **`metadata_id`**
544 : 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.
545
546 **`canonical_key`**
547 : `{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.
548
549 ### `muse code detect-refactor` (v2 output)
550
551 With `--json`, emits `schema_version: 2` with a richer classification:
552
553 ```json
554 {
555 "schema_version": 2,
556 "from_commit": "abc...",
557 "to_commit": "def...",
558 "total": 3,
559 "events": [
560 {
561 "old_address": "src/billing.py::compute_total",
562 "new_address": "src/billing.py::compute_invoice_total",
563 "old_kind": "function",
564 "new_kind": "function",
565 "exact_classification": "rename",
566 "inferred_refactor": "none",
567 "confidence": 1.0,
568 "evidence": ["body_hash matches a1b2c3d4"],
569 "old_content_id": "ab12cd34",
570 "new_content_id": "ef56gh78",
571 "old_body_hash": "a1b2c3d4",
572 "new_body_hash": "a1b2c3d4"
573 }
574 ]
575 }
576 ```
577
578 **`exact_classification`** values: `rename`, `move`, `rename+move`, `signature_only`, `impl_only`, `metadata_only`, `full_rewrite`, `unchanged`.
579
580 **`inferred_refactor`** values: `extract`, `inline`, `split`, `merge`, `none`.
581
582 ---
583
584 <a id="7-multi-agent-coordination-layer"></a>
585 ## 7. Multi-Agent Coordination Layer
586
587 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.
588
589 ### Storage Layout
590
591 ```
592 .muse/coordination/
593 reservations/<uuid>.json advisory symbol lease
594 intents/<uuid>.json declared operation before edit
595 ```
596
597 All records are **write-once** (never mutated) and use TTL-based expiry. Expired records are kept for audit purposes but ignored by all commands.
598
599 ---
600
601 ### `muse coord reserve ADDRESS... [OPTIONS]`
602
603 Announce intent to edit one or more symbol addresses.
604
605 ```
606 muse coord reserve src/billing.py::compute_total
607 muse coord reserve src/billing.py::f1 src/billing.py::f2 --run-id agent-007 --ttl 7200
608 muse coord reserve src/billing.py::compute_total --op rename
609 muse coord reserve src/billing.py::compute_total --json
610 ```
611
612 **Flags:**
613 - `--run-id ID` — identifier for this agent/run (default: random UUID)
614 - `--ttl SECONDS` — reservation expiry in seconds (default: 3600)
615 - `--op OPERATION` — declared operation: `rename`, `move`, `extract`, `modify`, `delete`
616 - `--json` — JSON output
617
618 **Conflict detection:** Warns (but never blocks) if any of the requested addresses are already reserved by another active reservation.
619
620 **Reservation schema (v1):**
621 ```json
622 {
623 "schema_version": 1,
624 "reservation_id": "<uuid>",
625 "run_id": "<agent-supplied ID>",
626 "branch": "<current branch>",
627 "addresses": ["src/billing.py::compute_total"],
628 "created_at": "2026-03-18T12:00:00+00:00",
629 "expires_at": "2026-03-18T13:00:00+00:00",
630 "operation": "rename"
631 }
632 ```
633
634 ---
635
636 ### `muse coord intent ADDRESS... --op OPERATION [OPTIONS]`
637
638 Declare a specific operation before executing it. More precise than a reservation; enables `muse coord forecast` to produce accurate conflict predictions.
639
640 ```
641 muse coord intent src/billing.py::compute_total --op rename --detail "rename to compute_invoice_total"
642 muse coord intent src/billing.py::compute_total --op modify --reservation-id <uuid>
643 ```
644
645 **Flags:**
646 - `--op OPERATION` *(required)* — `rename`, `move`, `extract`, `modify`, `delete`, `refactor`
647 - `--detail TEXT` — free-text description of the planned change
648 - `--reservation-id UUID` — link to an existing reservation
649 - `--run-id ID` — agent identifier
650 - `--json` — JSON output
651
652 **Intent schema (v1):**
653 ```json
654 {
655 "schema_version": 1,
656 "intent_id": "<uuid>",
657 "reservation_id": "<uuid or empty>",
658 "run_id": "<agent ID>",
659 "branch": "<current branch>",
660 "addresses": ["src/billing.py::compute_total"],
661 "operation": "rename",
662 "created_at": "2026-03-18T12:00:00+00:00",
663 "detail": "rename to compute_invoice_total"
664 }
665 ```
666
667 ---
668
669 ### `muse coord forecast [OPTIONS]`
670
671 Predict merge conflicts from active reservations and intents — **before** writing any code.
672
673 ```
674 muse coord forecast
675 muse coord forecast --branch feature-x
676 muse coord forecast --json
677 ```
678
679 **Conflict types detected:**
680
681 | Type | Confidence | Condition |
682 |---|---|---|
683 | `address_overlap` | 1.0 | Two reservations on the same symbol address |
684 | `blast_radius_overlap` | 0.75 | Reservations on symbols that call each other (via call graph) |
685 | `operation_conflict` | 0.9 | Two reservations declare incompatible operations (e.g. both `rename`) |
686
687 **Flags:**
688 - `--branch BRANCH` — restrict to reservations on this branch
689 - `--json` — structured conflict list
690
691 ---
692
693 ### `muse coord plan-merge OURS THEIRS [OPTIONS]`
694
695 Dry-run semantic merge plan — classify all symbol conflicts without writing anything.
696
697 ```
698 muse coord plan-merge main feature-x
699 muse coord plan-merge HEAD~5 HEAD --json
700 ```
701
702 **Output:** Classifies each diverging symbol into one of:
703 - `no_conflict` — diverged in disjoint symbols
704 - `symbol_edit_overlap` — both sides modified the same symbol
705 - `rename_edit` — one side renamed, the other modified
706 - `delete_use` — one side deleted a symbol still used by the other
707
708 **Flags:**
709 - `--json` — structured output with full classification details
710
711 ---
712
713 ### `muse coord shard --agents N [OPTIONS]`
714
715 Partition the codebase into N low-coupling work zones for parallel agent assignment.
716
717 ```
718 muse coord shard --agents 4
719 muse coord shard --agents 8 --language Python
720 muse coord shard --agents 4 --json
721 ```
722
723 **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).
724
725 **Flags:**
726 - `--agents N` *(required)* — number of shards
727 - `--language LANG` — restrict to files of this language
728 - `--json` — shard assignments as JSON
729
730 ---
731
732 ### `muse coord reconcile [OPTIONS]`
733
734 Recommend merge ordering and integration strategy from the current coordination state.
735
736 ```
737 muse coord reconcile
738 muse coord reconcile --json
739 ```
740
741 **Output:** For each active branch with reservations, recommends:
742 - **Merge order** — branches with fewer predicted conflicts should merge first
743 - **Integration strategy** — `fast-forward`, `rebase`, or `manual` (when conflicts are predicted)
744 - **Conflict hotspots** — addresses that appear in the most reservations
745
746 ---
747
748 <a id="8-merge-engine--architectural-enforcement"></a>
749 ## 8. Merge Engine & Architectural Enforcement
750
751 ### `ConflictRecord` — Structured Conflict Taxonomy
752
753 `MergeResult` now carries `conflict_records: list[ConflictRecord]` alongside the existing `conflicts: list[str]`. Each `ConflictRecord` provides structured metadata for programmatic conflict handling:
754
755 ```python
756 @dataclass
757 class ConflictRecord:
758 path: str
759 conflict_type: str = "file_level" # see taxonomy below
760 ours_summary: str = ""
761 theirs_summary: str = ""
762 addresses: list[str] = field(default_factory=list)
763 ```
764
765 **`conflict_type` taxonomy:**
766
767 | Value | Meaning |
768 |---|---|
769 | `symbol_edit_overlap` | Both branches modified the same symbol |
770 | `rename_edit` | One branch renamed, the other modified |
771 | `move_edit` | One branch moved, the other modified |
772 | `delete_use` | One branch deleted a symbol still used by the other |
773 | `dependency_conflict` | Conflicting changes to interdependent symbols |
774 | `file_level` | Legacy — no symbol-level information available |
775
776 ---
777
778 ### `muse code breakage`
779
780 Detect symbol-level structural breakage in the current working tree vs HEAD.
781
782 ```
783 muse code breakage
784 muse code breakage --language Python
785 muse code breakage --json
786 ```
787
788 **Checks performed:**
789
790 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).
791 2. **`missing_interface_method`** — a class body is missing a method that exists in the HEAD snapshot's version of that class.
792
793 **What it does NOT do:** Execute code, install packages, run mypy or a type checker, or access the network. Pure structural analysis.
794
795 **JSON output:**
796 ```json
797 {
798 "breakage_count": 2,
799 "issues": [
800 {
801 "issue_type": "stale_import",
802 "file": "src/billing.py",
803 "description": "imports compute_total from src/utils.py but compute_total was removed"
804 }
805 ]
806 }
807 ```
808
809 ---
810
811 ### `muse code invariants`
812
813 Enforce architectural rules declared in `.muse/invariants.toml`.
814
815 ```
816 muse code invariants
817 muse code invariants --commit HEAD~5
818 muse code invariants --json
819 ```
820
821 **Rule types:**
822
823 #### `no_cycles`
824 ```toml
825 [[rules]]
826 type = "no_cycles"
827 name = "no import cycles"
828 ```
829 The import graph must be a DAG. Reports every cycle as a violation.
830
831 #### `forbidden_dependency`
832 ```toml
833 [[rules]]
834 type = "forbidden_dependency"
835 name = "core must not import cli"
836 source_pattern = "muse/core/"
837 forbidden_pattern = "muse/cli/"
838 ```
839 Files matching `source_pattern` must not import from files matching `forbidden_pattern`.
840
841 #### `layer_boundary`
842 ```toml
843 [[rules]]
844 type = "layer_boundary"
845 name = "plugins must not import from cli"
846 lower = "muse/plugins/"
847 upper = "muse/cli/"
848 ```
849 Files in `lower` must not import from files in `upper` (enforces layered architecture).
850
851 #### `required_test`
852 ```toml
853 [[rules]]
854 type = "required_test"
855 name = "all billing functions must have tests"
856 source_pattern = "src/billing.py"
857 test_pattern = "tests/test_billing.py"
858 ```
859 Every public function in `source_pattern` must have a corresponding test function in `test_pattern` (matched by bare name).
860
861 **Bootstrapping:** If `.muse/invariants.toml` does not exist, `muse code invariants` creates it with a commented template and exits with a guided onboarding message.
862
863 ---
864
865 <a id="9-semantic-versioning"></a>
866 ## 9. Semantic Versioning
867
868 Muse automatically assigns semantic version bumps at commit time based on the `StructuredDelta`.
869
870 ### `SemVerBump`
871
872 ```python
873 SemVerBump = Literal["major", "minor", "patch", "none"]
874 ```
875
876 ### Inference rules (`infer_sem_ver_bump`)
877
878 | Change type | Bump | Breaking? |
879 |---|---|---|
880 | Delete a public symbol | `major` | yes — address added to `breaking_changes` |
881 | Rename a public symbol | `major` | yes — old address added to `breaking_changes` |
882 | `signature_only` change | `major` | yes — callers may break |
883 | Insert a new public symbol | `minor` | no |
884 | `impl_only` change (body only) | `patch` | no |
885 | `metadata_only` change | `none` | no |
886 | Formatting-only change | `none` | no |
887 | Non-public symbol changes | `patch` or `none` | no |
888
889 **Public** = name does not start with `_` and kind is `function`, `class`, `method`, or `async_function`.
890
891 ### Storage
892
893 Both `StructuredDelta` and `CommitRecord` carry:
894 - `sem_ver_bump: SemVerBump` (default `"none"`)
895 - `breaking_changes: list[str]` (default `[]`)
896
897 These fields are backward-compatible — pre-v2 commits read as `"none"` / `[]`.
898
899 ### `muse log` display
900
901 When a commit's `sem_ver_bump` is non-`none`, long-form `muse log` output appends:
902 ```
903 SemVer: MAJOR
904 Breaking: src/billing.py::compute_total, src/billing.py::Invoice (+2 more)
905 ```
906
907 ---
908
909 <a id="10-call-graph-tier-commands"></a>
910 ## 10. Call-Graph Tier Commands
911
912 ### `muse code impact ADDRESS [OPTIONS]`
913
914 Transitive blast-radius analysis — what else breaks if this function changes?
915
916 ```
917 muse code impact src/billing.py::compute_total
918 muse code impact src/billing.py::compute_total --commit HEAD~5
919 muse code impact src/billing.py::compute_total --json
920 ```
921
922 **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.
923
924 **Risk levels:** 🟢 (0–2 callers), 🟡 (3–9 callers), 🔴 (10+ callers).
925
926 ---
927
928 ### `muse code dead [OPTIONS]`
929
930 Dead code detection — symbols with no callers and no importers.
931
932 ```
933 muse code dead
934 muse code dead --kind function
935 muse code dead --exclude-tests
936 muse code dead --json
937 ```
938
939 **Detection logic:** A symbol is a dead-code candidate when:
940 1. Its bare name appears in no `ast.Call` node in the snapshot **and**
941 2. Its module is not imported anywhere in the snapshot.
942
943 **Distinction:** `definite_dead` (module never imported) vs `soft_dead` (module imported but function never called directly).
944
945 ---
946
947 ### `muse code coverage CLASS_ADDRESS [OPTIONS]`
948
949 Class interface call-coverage — which methods of a class are actually called?
950
951 ```
952 muse code coverage src/billing.py::Invoice
953 muse code coverage src/billing.py::Invoice --show-callers
954 muse code coverage src/billing.py::Invoice --json
955 ```
956
957 **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.
958
959 ---
960
961 ### `muse code deps ADDRESS_OR_FILE [OPTIONS]`
962
963 Import graph + call-graph analysis.
964
965 ```
966 muse code deps src/billing.py
967 muse code deps src/billing.py --reverse
968 muse code deps src/billing.py::compute_total
969 muse code deps src/billing.py::compute_total --reverse
970 muse code deps src/billing.py --commit v1.0 --json
971 ```
972
973 **File mode:** Lists all `import`-kind symbols from the file (what does it import?). With `--reverse`: which other files import this one.
974
975 **Symbol mode** (`address` contains `::`): Python-only call extraction — which functions does this function call? With `--reverse`: which functions call this one.
976
977 ---
978
979 ### `muse code find-symbol [OPTIONS]`
980
981 Cross-commit, cross-branch symbol search by hash, name, or kind.
982
983 ```
984 muse code find-symbol --hash a3f2c9
985 muse code find-symbol --name compute_total
986 muse code find-symbol --name compute_* --kind function
987 muse code find-symbol --hash a3f2c9 --all-branches --first
988 muse code find-symbol --name validate --json
989 ```
990
991 **Flags:**
992 - `--hash HEX` — match `content_id` prefix (exact body match across history)
993 - `--name NAME` — exact name or prefix glob with `*`
994 - `--kind KIND` — restrict to symbol kind
995 - `--all-branches` — also scan all branch tips in `.muse/refs/heads/`
996 - `--first` — deduplicate on `content_id`, keeping only the first appearance
997 - `--json` — structured output
998
999 ---
1000
1001 ### `muse code patch ADDRESS SOURCE [OPTIONS]`
1002
1003 Surgical semantic patch — replace exactly one named symbol in the working tree.
1004
1005 ```
1006 muse code patch src/billing.py::compute_total new_impl.py
1007 echo "def compute_total(x): return x * 2" | muse code patch src/billing.py::compute_total -
1008 muse code patch src/billing.py::compute_total new_impl.py --dry-run
1009 muse code patch src/billing.py::compute_total new_impl.py --json
1010 ```
1011
1012 **Syntax validation:** Before writing, validates the replacement source with:
1013 - `ast.parse` for Python
1014 - `tree-sitter` CST error-node check for all 11 supported languages
1015
1016 Rejects the patch and exits non-zero if the source has syntax errors.
1017
1018 **Flags:**
1019 - `--body, -b FILE` *(required)* — file containing the replacement source (`-` for stdin)
1020 - `--dry-run, -n` — print what would change without writing
1021 - `--json` — emit result as JSON for agent consumption
1022
1023 **JSON output:**
1024
1025 ```json
1026 {
1027 "address": "src/billing.py::compute_total",
1028 "file": "src/billing.py",
1029 "lines_replaced": 12,
1030 "new_lines": 9,
1031 "dry_run": false
1032 }
1033 ```
1034
1035 **Security:** The file path component of ADDRESS is validated via `contain_path()` before any disk access. Paths that escape the repo root (e.g. `../../etc/passwd::foo`) are rejected with exit 1.
1036
1037 ---
1038
1039 ### `muse grep PATTERN [OPTIONS]`
1040
1041 Search the typed symbol graph by name — not file text. Every result is a real symbol declaration; no false positives from comments, string literals, or call sites.
1042
1043 ```
1044 muse grep validate
1045 muse grep "^handle" --regex
1046 muse grep Invoice --kind class
1047 muse grep compute --language Go
1048 muse grep total --commit HEAD~5
1049 muse grep validate --json
1050 ```
1051
1052 **Flags:**
1053
1054 | Flag | Short | Description |
1055 |---|---|---|
1056 | `--regex, -e` | | Treat PATTERN as a Python regex (default: substring match) |
1057 | `--kind KIND, -k` | | Restrict to symbols of this kind (function, class, method, …) |
1058 | `--language LANG, -l` | | Restrict to files of this language (Python, Go, …) |
1059 | `--commit REF, -c` | | Search a historical commit instead of HEAD |
1060 | `--hashes` | | Include 8-char content-ID prefix in output |
1061 | `--json` | | Emit results as JSON |
1062
1063 **JSON output:**
1064
1065 ```json
1066 [
1067 {
1068 "address": "src/auth.py::validate_token",
1069 "kind": "function",
1070 "name": "validate_token",
1071 "qualified_name": "validate_token",
1072 "file": "src/auth.py",
1073 "lineno": 14,
1074 "language": "Python",
1075 "content_id": "cb4afa1234567890..."
1076 }
1077 ]
1078 ```
1079
1080 **Security:** Patterns are capped at 512 characters to prevent ReDoS. Invalid regex syntax is caught and reported as exit 1 rather than crashing.
1081
1082 ---
1083
1084 ### `muse code-check [COMMIT] [OPTIONS]`
1085
1086 Enforce semantic code invariants against a commit snapshot.
1087
1088 ```
1089 muse code-check # check HEAD
1090 muse code-check abc1234 # check specific commit
1091 muse code-check --strict # exit 1 on any error-severity violation
1092 muse code-check --json # machine-readable JSON output
1093 muse code-check --rules my_rules.toml # custom rules file inside the repo
1094 ```
1095
1096 **Flags:**
1097
1098 | Flag | Description |
1099 |---|---|
1100 | `COMMIT` | Commit ID to check (default: HEAD) |
1101 | `--strict` | Exit 1 when any error-severity violation is found |
1102 | `--json` | Emit machine-readable JSON |
1103 | `--rules FILE` | Path to a TOML invariants file **inside the repo** (default: `.muse/code_invariants.toml`) |
1104
1105 **Security:** `--rules FILE` is validated via `contain_path()` — paths that escape the repo root are rejected with exit 1.
1106
1107 ---
1108
1109 <a id="11-architecture-internals"></a>
1110 ## 11. Architecture Internals
1111
1112 ### Module Map
1113
1114 ```
1115 muse/
1116 plugins/code/
1117 plugin.py MidiPlugin → CodePlugin (MuseDomainPlugin + StructuredMergePlugin)
1118 ast_parser.py Python AST → SymbolRecord; validate_syntax() for all 11 languages
1119 symbol_diff.py diff_symbol_trees() — O(n) diffing, rename/move annotation
1120 _query.py symbols_for_snapshot(), walk_commits(), language_of()
1121 _predicate.py Predicate DSL parser — tokenise → recursive descent → Predicate callable
1122 _callgraph.py ForwardGraph, ReverseGraph, build_*, transitive_callers BFS
1123 _refactor_classify.py classify_exact(), classify_composite(), RefactorClassification
1124 core/
1125 coordination.py Reservation, Intent, create/load helpers, .muse/coordination/
1126 indices.py SymbolHistoryIndex, HashOccurrenceIndex, save/load/rebuild
1127 ```
1128
1129 ### Language Support
1130
1131 | Language | Extension(s) | Parser | Symbol types |
1132 |---|---|---|---|
1133 | Python | `.py` | `ast` (stdlib) | function, async_function, class, method, variable, import |
1134 | JavaScript | `.js` `.jsx` `.mjs` `.cjs` | tree-sitter | function, class, method |
1135 | TypeScript | `.ts` `.tsx` | tree-sitter | function, class, method, interface, type_alias, enum |
1136 | Go | `.go` | tree-sitter | function (method qualified as `Type.Method`) |
1137 | Rust | `.rs` | tree-sitter | function (impl method qualified as `Type.method`) |
1138 | Java | `.java` | tree-sitter | class, interface, method, constructor, enum |
1139 | C | `.c` `.h` | tree-sitter | function_definition |
1140 | C++ | `.cpp` `.cc` `.cxx` `.hpp` | tree-sitter | function, class, struct |
1141 | C# | `.cs` | tree-sitter | class, interface, struct, method, constructor, enum |
1142 | Ruby | `.rb` | tree-sitter | class, module, method, singleton_method |
1143 | Kotlin | `.kt` `.kts` | tree-sitter | function, class, method |
1144 | Markdown / RST | `.md` `.rst` `.txt` | tree-sitter-markdown | section (ATX headings) |
1145 | HTML | `.html` `.htm` | tree-sitter-html | section (semantic elements, id-bearing elements) |
1146 | CSS / SCSS | `.css` `.scss` | tree-sitter-css | rule (rule-sets, @keyframes, @media) |
1147 | TOML | `.toml` | `tomllib` (stdlib, zero deps) | section (`[table]`, `[[array]]`), variable (scalar key-value) |
1148
1149 ### Layer Rules
1150
1151 - `muse/core/*` is domain-agnostic — never imports from `muse/plugins/*`
1152 - `muse/cli/commands/*` are thin — delegate all logic to `muse/core/*` or plugin helpers
1153 - `muse/plugins/code/*` is the only layer that imports domain-specific AST logic
1154 - `muse/core/coordination.py` and `muse/core/indices.py` are domain-agnostic helpers
1155
1156 ---
1157
1158 <a id="12-type-reference"></a>
1159 ## 12. Type Reference
1160
1161 ### `SymbolRecord` (TypedDict)
1162
1163 ```python
1164 class SymbolRecord(TypedDict):
1165 kind: str # function | class | method | variable | import | …
1166 name: str # bare name
1167 qualified_name: str # dotted path (e.g. MyClass.save)
1168 lineno: int
1169 end_lineno: int
1170 content_id: str # SHA-256 of full normalized AST
1171 body_hash: str # SHA-256 of body only
1172 signature_id: str # SHA-256 of signature only
1173 metadata_id: str # SHA-256 of decorators + async + bases (v2, "" for pre-v2)
1174 canonical_key: str # {file}#{scope}#{kind}#{name}#{lineno} (v2, "" for pre-v2)
1175 ```
1176
1177 ### `StructuredDelta`
1178
1179 ```python
1180 class StructuredDelta(TypedDict):
1181 domain: str
1182 ops: list[DomainOp]
1183 summary: str
1184 sem_ver_bump: SemVerBump # default "none"
1185 breaking_changes: list[str] # default []
1186 ```
1187
1188 ### `DomainOp` union
1189
1190 ```python
1191 DomainOp = InsertOp | DeleteOp | ReplaceOp | MoveOp | PatchOp
1192 ```
1193
1194 Each op is a `TypedDict` discriminated by a `Literal` `"op"` field.
1195
1196 ### `ConflictRecord` (dataclass)
1197
1198 ```python
1199 @dataclass
1200 class ConflictRecord:
1201 path: str
1202 conflict_type: str = "file_level"
1203 ours_summary: str = ""
1204 theirs_summary: str = ""
1205 addresses: list[str] = field(default_factory=list)
1206 ```
1207
1208 ### `Reservation`
1209
1210 ```python
1211 class Reservation:
1212 reservation_id: str
1213 run_id: str
1214 branch: str
1215 addresses: list[str]
1216 created_at: datetime
1217 expires_at: datetime
1218 operation: str | None
1219 def is_active(self) -> bool: ...
1220 def to_dict(self) -> dict[str, str | int | list[str] | None]: ...
1221 @classmethod
1222 def from_dict(cls, d) -> Reservation: ...
1223 ```
1224
1225 ### `Intent`
1226
1227 ```python
1228 class Intent:
1229 intent_id: str
1230 reservation_id: str
1231 run_id: str
1232 branch: str
1233 addresses: list[str]
1234 operation: str
1235 created_at: datetime
1236 detail: str
1237 def to_dict(self) -> dict[str, str | int | list[str]]: ...
1238 @classmethod
1239 def from_dict(cls, d) -> Intent: ...
1240 ```
1241
1242 ### `SemVerBump`
1243
1244 ```python
1245 SemVerBump = Literal["major", "minor", "patch", "none"]
1246 ```
1247
1248 ### `Predicate`
1249
1250 ```python
1251 Predicate = Callable[[str, SymbolRecord], bool]
1252 # first arg: file_path
1253 # second arg: SymbolRecord
1254 # returns: True if the symbol matches the predicate
1255 ```
1256
1257 ### `ExactClassification`
1258
1259 ```python
1260 ExactClassification = Literal[
1261 "rename", "move", "rename+move",
1262 "signature_only", "impl_only", "metadata_only",
1263 "full_rewrite", "unchanged",
1264 ]
1265 ```
1266
1267 ### `InferredRefactor`
1268
1269 ```python
1270 InferredRefactor = Literal["extract", "inline", "split", "merge", "none"]
1271 ```
1272
1273 ---
1274
1275 ## Further Reading
1276
1277 - [Plugin Authoring Guide](plugin-authoring-guide.md) — implementing `MuseDomainPlugin`
1278 - [Type Contracts](type-contracts.md) — strict typing rules and enforcement
1279 - [CRDT Reference](crdt-reference.md) — CRDT and OT merge primitives
1280 - [Demo — Code](../demo/demo-code.md) — full narrative walkthrough of all code commands
1281 - [Demo — MIDI](../demo/midi-demo.md) — MIDI domain demo walkthrough