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