#!/usr/bin/env python3 """Muse Domain Registry — standalone HTML generator. Produces a self-contained, shareable page that explains the MuseDomainPlugin protocol, shows the registered plugin ecosystem, and guides developers through scaffolding and publishing their own domain plugin. Stand-alone usage ----------------- python tools/render_domain_registry.py python tools/render_domain_registry.py --out artifacts/domain_registry.html """ import json import pathlib import subprocess import sys _ROOT = pathlib.Path(__file__).resolve().parent.parent # --------------------------------------------------------------------------- # Live domain data from the CLI # --------------------------------------------------------------------------- def _compute_crdt_demos() -> list[dict]: """Run the four CRDT primitives live and return formatted demo output.""" sys.path.insert(0, str(_ROOT)) try: from muse.core.crdts import AWMap, GCounter, LWWRegister, ORSet, RGA, VectorClock # ORSet base, _ = ORSet().add("annotation-GO:0001234") a, _ = base.add("annotation-GO:0001234") b = base.remove("annotation-GO:0001234", base.tokens_for("annotation-GO:0001234")) merged = a.join(b) orset_out = "\n".join([ "ORSet — add-wins concurrent merge:", f" base elements: {sorted(base.elements())}", f" A re-adds → elements: {sorted(a.elements())}", f" B removes → elements: {sorted(b.elements())}", f" join(A, B) → elements: {sorted(merged.elements())}", " [A's new token is not tombstoned — add always wins]", ]) # LWWRegister ra = LWWRegister.from_dict({"value": "80 BPM", "timestamp": 1.0, "author": "agent-A"}) rb = LWWRegister.from_dict({"value": "120 BPM", "timestamp": 2.0, "author": "agent-B"}) rm = ra.join(rb) lww_out = "\n".join([ "LWWRegister — last-write-wins scalar:", f" Agent A writes: '{ra.read()}' at t=1.0", f" Agent B writes: '{rb.read()}' at t=2.0 (later)", f" join(A, B) → '{rm.read()}' [higher timestamp wins]", " join(B, A) → same result [commutativity]", ]) # GCounter ca = GCounter().increment("agent-A").increment("agent-A") cb = GCounter().increment("agent-B").increment("agent-B").increment("agent-B") cm = ca.join(cb) gc_out = "\n".join([ "GCounter — grow-only distributed counter:", f" Agent A x2 → A slot: {ca.value_for('agent-A')}", f" Agent B x3 → B slot: {cb.value_for('agent-B')}", f" join(A, B) global value: {cm.value()}", " [monotonically non-decreasing — joins never lose counts]", ]) # VectorClock va = VectorClock().increment("agent-A") vb = VectorClock().increment("agent-B") vm = va.merge(vb) vc_out = "\n".join([ "VectorClock — causal ordering:", f" Agent A: {va.to_dict()}", f" Agent B: {vb.to_dict()}", f" concurrent_with(A, B): {va.concurrent_with(vb)}", f" merge(A, B): {vm.to_dict()} [component-wise max]", ]) elem = "annotation-GO:0001234" short = "GO:0001234" orset_html = f"""
Replica A
+ add("{short}")
→ {{{short}}}
Replica B
× remove("{short}")
→ {{}}
join(A, B) {{{short}}} add-wins — A’s new token survives
""" lww_html = f"""
t=1.0 A "{ra.read()}"
t=2.0 B "{rb.read()}" latest ↑
join(A,B) = join(B,A) "{rm.read()}" commutative — higher timestamp always wins
""" a_val = ca.value_for("agent-A") b_val = cb.value_for("agent-B") total = cm.value() a_pct = int(a_val / total * 100) b_pct = int(b_val / total * 100) gc_html = f"""
A ×{a_val}
{a_val}
B ×{b_val}
{b_val}
join(A, B) global {total} component-wise max — monotonically non-decreasing
""" concurrent = va.concurrent_with(vb) merged_d = vm.to_dict() vc_html = f"""
Agent A
A:1
B:0
Agent B
A:0
B:1
=
merge
{"".join(f'
{k.split("-")[1].upper()}:{v}
' for k, v in sorted(merged_d.items()))}
concurrent_with(A, B) = {concurrent}
component-wise max — causal happens-before tracking
""" # RGA — ordered note sequence: A inserts C4+E4, B inserts G4 concurrently rga_a = RGA() rga_a = rga_a.insert(None, "hash-C4", element_id="1@agent-A") rga_a = rga_a.insert("1@agent-A", "hash-E4", element_id="2@agent-A") rga_b = RGA() rga_b = rga_b.insert(None, "hash-C4", element_id="1@agent-A") # same start rga_b = rga_b.insert("1@agent-A", "hash-G4", element_id="1@agent-B") # concurrent rga_m = rga_a.join(rga_b) rga_seq_a = ["C4", "E4"] rga_seq_b = ["C4", "G4"] _rga_hash_to_note = {"hash-C4": "C4", "hash-E4": "E4", "hash-G4": "G4"} rga_seq_m = [_rga_hash_to_note.get(h, h) for h in rga_m.to_sequence()] rga_out = "\n".join([ "RGA — ordered sequence (Google-Docs-style):", f" Agent A sequence: {rga_seq_a}", f" Agent B sequence: {rga_seq_b}", f" join(A, B): {rga_seq_m} [deterministic, ID-ordered]", " Tombstones kept — deletions never lose causal history", ]) rga_html = """
Replica A
+ insert("C4", id=1@A)
+ insert("E4", id=2@A)
→ [C4, E4]
Replica B
+ insert("C4", id=1@A)
+ insert("G4", id=1@B) concurrent
→ [C4, G4]
join(A, B) [C4, E4, G4] ID-ordered — larger ID wins leftmost; all elements preserved
""" # AWMap — key-value map: A sets "tempo", B sets "key_sig", both survive aw_a = AWMap() aw_a = aw_a.set("tempo", "120 BPM") aw_b = AWMap() aw_b = aw_b.set("key_sig", "C major") aw_m = aw_a.join(aw_b) aw_keys = sorted(aw_m.keys()) awmap_out = "\n".join([ "AWMap — add-wins key/value map:", f" Agent A sets: {{tempo: '120 BPM'}}", f" Agent B sets: {{key_sig: 'C major'}}", f" join(A, B) keys: {aw_keys}", " Both additions survive — concurrent removes cannot win", ]) awmap_html = """
Replica A
+ set("tempo", "120 BPM")
→ {tempo}
Replica B
+ set("key_sig", "C major")
→ {key_sig}
join(A, B) {key_sig, tempo} add-wins — concurrent removes cannot evict new tokens
""" return [ {"type": "ORSet", "sub": "Observed-Remove Set", "color": "#bc8cff", "icon": _ICONS["union"], "output": orset_out, "html_output": orset_html}, {"type": "LWWRegister", "sub": "Last-Write-Wins Register", "color": "#58a6ff", "icon": _ICONS["edit"], "output": lww_out, "html_output": lww_html}, {"type": "GCounter", "sub": "Grow-Only Distributed Counter", "color": "#3fb950", "icon": _ICONS["arrow-up"], "output": gc_out, "html_output": gc_html}, {"type": "VectorClock", "sub": "Causal Ordering", "color": "#f9a825", "icon": _ICONS["git-branch"], "output": vc_out, "html_output": vc_html}, {"type": "RGA", "sub": "Replicated Growable Array", "color": "#f9a825", "icon": _ICONS["layers"], "output": rga_out, "html_output": rga_html}, {"type": "AWMap", "sub": "Add-Wins Map", "color": "#3fb950", "icon": _ICONS["code"], "output": awmap_out, "html_output": awmap_html}, ] except Exception as exc: print(f" ⚠ CRDT demo failed ({exc}); using static fallback") return [] def _load_domains() -> list[dict]: """Run `muse domains --json` and return parsed output.""" try: result = subprocess.run( [sys.executable, "-m", "muse", "domains", "--json"], capture_output=True, text=True, cwd=str(_ROOT), timeout=15, ) if result.returncode == 0: raw = result.stdout.strip() data: list[dict] = json.loads(raw) return data except Exception: pass # Fallback: static reference data return [ { "domain": "midi", "active": "true", "capabilities": ["Typed Deltas", "Domain Schema", "OT Merge", "CRDT Primitives", "MidiRGA"], "schema": { "schema_version": "1", "merge_mode": "three_way", "description": "MIDI file versioning with 21-dimension structured merge — notes, CC, pitch bend, tempo, time signatures, and more. Each dimension merges independently. MidiRGA provides voice-aware CRDT ordering (bass→tenor→alto→soprano) for concurrent note edits.", "dimensions": [ {"name": "notes", "description": "Note-on/off events (pitch, velocity, channel, timing)"}, {"name": "control_change", "description": "CC curves: modulation, sustain, expression, pan"}, {"name": "pitch_bend", "description": "Pitch bend envelope per channel"}, {"name": "tempo", "description": "Tempo map (BPM changes over time)"}, {"name": "time_signature", "description": "Metre changes across the piece"}, {"name": "key_signature", "description": "Key and mode declarations"}, {"name": "program_change", "description": "Instrument (patch) assignments per channel"}, {"name": "aftertouch", "description": "Channel and polyphonic pressure"}, {"name": "sysex", "description": "System-exclusive device messages"}, {"name": "track_name", "description": "Human-readable track labels"}, ], }, }, { "domain": "code", "active": "true", "capabilities": ["Typed Deltas", "Domain Schema", "OT Merge", ".museattributes"], "schema": { "schema_version": "1", "merge_mode": "symbol_ot", "description": "Source-code versioning with symbol-level operational-transform merge. Tree-sitter parses 11 languages into ASTs; functions, classes, and imports merge independently. .museattributes gives per-path strategy control.", "dimensions": [ {"name": "functions", "description": "Function and method definitions"}, {"name": "classes", "description": "Class and struct declarations"}, {"name": "imports", "description": "Import and module-level declarations"}, {"name": "variables", "description": "Module-level variable and constant assignments"}, {"name": "expressions", "description": "Top-level expression statements"}, ], }, }, ] # --------------------------------------------------------------------------- # Scaffold template (shown in the "Build in 3 steps" section) # --------------------------------------------------------------------------- _TYPED_DELTA_EXAMPLE = """\ # muse show --json (any commit, any domain) { "commit_id": "b26f3c99", "message": "Resolve: integrate shared-state (A+B reconciled)", "operations": [ { "op_type": "ReplaceOp", "address": "shared-state.mid", "before_hash": "a1b2c3d4", "after_hash": "e5f6g7h8", "dimensions": ["structural"] }, { "op_type": "InsertOp", "address": "beta-a.mid", "after_hash": "09ab1234", "dimensions": ["rhythmic", "dynamic"] } ], "summary": { "inserted": 1, "replaced": 1, "deleted": 0 } }""" _SCAFFOLD_SNIPPET = """\ from muse.domain import ( MuseDomainPlugin, LiveState, StateSnapshot, StateDelta, DriftReport, MergeResult, DomainSchema, ) class GenomicsPlugin(MuseDomainPlugin): \"\"\"Version control for genomic sequences.\"\"\" def snapshot(self, live_state: LiveState) -> StateSnapshot: # Serialize current genome state to a content-addressable blob raise NotImplementedError def diff(self, base: StateSnapshot, target: StateSnapshot) -> StateDelta: # Compute minimal delta between two snapshots raise NotImplementedError def merge(self, base: StateSnapshot, left: StateSnapshot, right: StateSnapshot) -> MergeResult: # Three-way merge — surface conflicts per dimension raise NotImplementedError def drift(self, committed: StateSnapshot, live: LiveState) -> DriftReport: # Detect uncommitted changes in the working state raise NotImplementedError def apply(self, delta: StateDelta, live_state: LiveState) -> LiveState: # Reconstruct historical state from a delta raise NotImplementedError def schema(self) -> DomainSchema: # Declare dimensions — drives diff algorithm selection raise NotImplementedError """ # --------------------------------------------------------------------------- # SVG icon library — Lucide/Feather style, stroke="currentColor", no fixed size # --------------------------------------------------------------------------- def _icon(paths: str) -> str: """Wrap SVG paths in a standard icon shell.""" return ( '' + paths + "" ) _ICONS: dict[str, str] = { # Domains "midi": _icon(''), "genomics": _icon(''), "cube": _icon(''), "trending": _icon(''), "atom": _icon(''), "plus": _icon(''), "activity": _icon(''), "pen-tool": _icon(''), # Distribution "terminal": _icon(''), "package": _icon(''), "globe": _icon(''), # Engine capabilities "code": _icon(''), "layers": _icon(''), "git-merge": _icon(''), "zap": _icon(''), # MuseHub features "search": _icon(''), "lock": _icon(''), # CRDT primitives "union": _icon(''), "edit": _icon(''), "arrow-up": _icon(''), "git-branch": _icon(''), # OT scenario outcome badges "check-circle":_icon(''), "x-circle": _icon(''), } # --------------------------------------------------------------------------- # Planned / aspirational domains # --------------------------------------------------------------------------- _PLANNED_DOMAINS = [ { "name": "Genomics", "icon": _ICONS["genomics"], "status": "planned", "tagline": "Version sequences, variants, and annotations", "dimensions": ["sequence", "variants", "annotations", "metadata"], "color": "#3fb950", }, { "name": "3D / Spatial", "icon": _ICONS["cube"], "status": "planned", "tagline": "Merge spatial fields, meshes, and simulation frames", "dimensions": ["geometry", "materials", "physics", "temporal"], "color": "#58a6ff", }, { "name": "Financial", "icon": _ICONS["trending"], "status": "planned", "tagline": "Track model versions, alpha signals, and risk state", "dimensions": ["signals", "positions", "risk", "parameters"], "color": "#f9a825", }, { "name": "Scientific Simulation", "icon": _ICONS["atom"], "status": "planned", "tagline": "Snapshot simulation state across timesteps and parameter spaces", "dimensions": ["state", "parameters", "observables", "checkpoints"], "color": "#ab47bc", }, { "name": "Your Domain", "icon": _ICONS["plus"], "status": "yours", "tagline": "Six methods. Any multidimensional state. Full VCS for free.", "dimensions": ["your_dim_1", "your_dim_2", "..."], "color": "#4f8ef7", }, ] # --------------------------------------------------------------------------- # Distribution model description # --------------------------------------------------------------------------- _DISTRIBUTION_LEVELS = [ { "tier": "Local", "icon": _ICONS["terminal"], "title": "Local plugin (right now)", "color": "#3fb950", "steps": [ "muse domains --new <name>", "Implement 6 methods in muse/plugins/<name>/plugin.py", "Register in muse/plugins/registry.py", "muse init --domain <name>", ], "desc": "Works today. Scaffold → implement → register. " "Your plugin lives alongside the core.", }, { "tier": "Shareable", "icon": _ICONS["package"], "title": "pip-installable package (right now)", "color": "#58a6ff", "steps": [ "Package your plugin as a Python module", "pip install git+https://github.com/you/muse-plugin-genomics", "Register the entry-point in pyproject.toml", "muse init --domain genomics", ], "desc": "Share your plugin as a standard Python package. " "Anyone with pip can install and use it.", }, { "tier": "MuseHub", "icon": _ICONS["globe"], "title": "Centralized registry (coming — MuseHub)", "color": "#bc8cff", "steps": [ "musehub publish muse-plugin-genomics", "musehub search genomics", "muse init --domain @musehub/genomics", "Browse plugins at musehub.io", ], "desc": "MuseHub is a planned centralized registry — npm for Muse plugins. " "Versioned, searchable, one-command install.", }, ] # --------------------------------------------------------------------------- # HTML template # --------------------------------------------------------------------------- def _render_capability_card(cap: dict) -> str: color = cap["color"] body = ( cap["html_output"] if "html_output" in cap else f'
{cap["output"]}
' ) return f"""
{cap['icon']} {cap['type']} {cap['sub']}
{body}
""" def _render_domain_card(d: dict) -> str: domain = d.get("domain", "unknown") active = d.get("active") == "true" schema = d.get("schema", {}) desc = schema.get("description", "") dims = schema.get("dimensions", []) caps = d.get("capabilities", []) cap_html = " ".join( f'{c}' for c in caps ) dim_html = " · ".join( f'{dim["name"]}' for dim in dims ) status_cls = "active-badge" if active else "reg-badge" status_text = "● active" if active else "○ registered" dot = '' if active else "" short_desc = desc[:150] + ("…" if len(desc) > 150 else "") return f"""
{status_text} {domain} {dot}

{short_desc}

{cap_html}
Dimensions: {dim_html}
""" def _render_planned_card(p: dict) -> str: dims = " · ".join(f'{d}' for d in p["dimensions"]) cls = "planned-card yours" if p["status"] == "yours" else "planned-card" return f"""
{p['icon']}
{p['name']}
{p['tagline']}
{dims}
{'Build it →' if p["status"] == "yours" else 'coming soon'}
""" def _render_dist_card(d: dict) -> str: steps = "".join( f'
  • {s}
  • ' for s in d["steps"] ) return f"""
    {d['icon']}
    {d['tier']}
    {d['title']}

    {d['desc']}

      {steps}
    """ def render(output_path: pathlib.Path) -> None: """Generate the domain registry HTML page.""" print(" Loading live domain data...") domains = _load_domains() print(f" Found {len(domains)} registered domain(s)") print(" Computing live CRDT demos...") crdt_demos = _compute_crdt_demos() active_domains_html = "\n".join(_render_domain_card(d) for d in domains) planned_html = "\n".join(_render_planned_card(p) for p in _PLANNED_DOMAINS) dist_html = "\n".join(_render_dist_card(d) for d in _DISTRIBUTION_LEVELS) crdt_cards_html = "\n".join(_render_capability_card(c) for c in crdt_demos) html = _HTML_TEMPLATE.replace("{{ACTIVE_DOMAINS}}", active_domains_html) html = html.replace("{{PLANNED_DOMAINS}}", planned_html) html = html.replace("{{DIST_CARDS}}", dist_html) html = html.replace("{{SCAFFOLD_SNIPPET}}", _SCAFFOLD_SNIPPET) html = html.replace("{{TYPED_DELTA_EXAMPLE}}", _TYPED_DELTA_EXAMPLE) html = html.replace("{{CRDT_CARDS}}", crdt_cards_html) html = html.replace("{{DIFF_ALGEBRA}}", _DIFF_ALGEBRA_HTML) # Inject SVG icons into template placeholders _ICON_SLOTS: dict[str, str] = { "MUSIC": _ICONS["midi"], "GENOMICS": _ICONS["genomics"], "CUBE": _ICONS["cube"], "TRENDING": _ICONS["trending"], "ATOM": _ICONS["atom"], "PLUS": _ICONS["plus"], "ACTIVITY": _ICONS["activity"], "PEN_TOOL": _ICONS["pen-tool"], "CODE": _ICONS["code"], "LAYERS": _ICONS["layers"], "GIT_MERGE": _ICONS["git-merge"], "ZAP": _ICONS["zap"], "GLOBE": _ICONS["globe"], "SEARCH": _ICONS["search"], "PACKAGE": _ICONS["package"], "LOCK": _ICONS["lock"], "CHECK_CIRCLE": _ICONS["check-circle"], "X_CIRCLE": _ICONS["x-circle"], } for slot, svg in _ICON_SLOTS.items(): html = html.replace(f"{{{{ICON_{slot}}}}}", svg) output_path.write_text(html, encoding="utf-8") size_kb = output_path.stat().st_size // 1024 print(f" HTML written ({size_kb}KB) → {output_path}") # Also write as index.html so the domain registry IS the landing page. index_path = output_path.parent / "index.html" index_path.write_text(html, encoding="utf-8") print(f" Landing page mirrored → {index_path}") # --------------------------------------------------------------------------- # Diff Algebra section — five algorithm visualizations + StructuredDelta flow # --------------------------------------------------------------------------- _DIFF_ALGEBRA_HTML = """
    Diff Algebra

    Five Algebras. One Typed Result.

    The engine selects the algorithm per dimension from your plugin’s schema(). You declare the shape — the engine handles identity, diffing, and merge selection automatically.

    Sequence Myers / LCS on SHA-256 IDs
    notes · nucleotides · animation frames · git objects
    before
    a1b2
    C4
    c3d4
    E4
    e5f6
    G4
    g7h8
    B♭4
    = match
    × DeleteOp
    = match
    ↓ MoveOp
    a1b2
    C4
    k1l2
    F4
    e5f6
    G4
    n5o6
    A4
    g7h8
    B♭4
    + InsertOp
    + InsertOp
    ↑ arrived
    after
    Identity is hash-based: two elements are equal iff their SHA-256 hashes match — content is never inspected by the core. Delete + insert pairs sharing the same hash are collapsed into MoveOps in a post-pass.
    Tree Zhang-Shasha / GumTree
    scene graphs · ASTs · track hierarchies
    BEFORE session intro verse C4 E4 G4 MoveOp + InsertOp AFTER session intro verse C4 E4 G4 A4 moved inserted
    Edit distance over node hierarchy — moves preserve subtree identity across parent changes.
    Tensor Sparse / block numerical diff
    sim state · voxel grids · weight matrices
    t = 0
    t = 1 (Δ)
    |Δ| = 0 |Δ| ≤ ε (within threshold) |Δ| > ε → PatchOp
    Configurable ε threshold. Sparse mode records only changed blocks — efficient for large tensors.
    Set Set algebra · add / remove
    annotations · tags · gene ontology terms
    before
    GO:0001234 GO:0005634 GO:0006915 GO:0016020
    × del
    + ins
    after
    GO:0001234 GO:0005634 GO:0016020 GO:0042592
    Unordered — no position tracking. Pure membership delta: {removed} and {added}.
    Map Recursive key-by-key delegation
    metadata · configs · nested structures
    tempo 120 → 140 scalar → PatchOp
    notes […] → […′] sequence → LCS
    tags {…} → {…′} set → algebra
    author "Bach" = "Bach" unchanged
    Each key is diffed by whichever algorithm matches its declared type — recursively, to arbitrary depth.
    diff() → StructuredDelta all five algorithms produce the same typed operation list
    InsertOp DeleteOp MoveOp ReplaceOp PatchOp
    merge_mode: “three_way”
    Operational Transformation — independent ops commute automatically; conflicting ops surface for human resolution
    or
    merge_mode: “crdt”
    CRDT join() — convergent, no coordination required; any two replicas always reach the same state
    """ # --------------------------------------------------------------------------- # Large HTML template # --------------------------------------------------------------------------- _HTML_TEMPLATE = """\ Muse — Version Anything

    muse

    Version Anything

    One protocol. Any domain. Six methods between you and a complete version control system — branching, merging, conflict resolution, time-travel, and typed diffs — for free.

    {{ICON_MUSIC}} midi {{ICON_CODE}} code {{ICON_GENOMICS}} genomics {{ICON_CUBE}} 3d-spatial {{ICON_TRENDING}} financial {{ICON_ATOM}} simulation {{ICON_ACTIVITY}} proteomics {{ICON_PEN_TOOL}} cad {{ICON_ZAP}} game-state {{ICON_PLUS}} your-domain {{ICON_MUSIC}} midi {{ICON_CODE}} code {{ICON_GENOMICS}} genomics {{ICON_CUBE}} 3d-spatial {{ICON_TRENDING}} financial {{ICON_ATOM}} simulation {{ICON_ACTIVITY}} proteomics {{ICON_PEN_TOOL}} cad {{ICON_ZAP}} game-state {{ICON_PLUS}} your-domain
    The Contract

    The MuseDomainPlugin Protocol

    Every domain — MIDI, source code, genomics, 3D spatial, financial models — implements the same six-method protocol. The core engine handles everything else: content-addressed storage, DAG, branches, log, merge base, cherry-pick, revert, stash, tags.

    6methods to implement
    14CLI commands, free
    domains possible
    0core changes needed
    Method
    Signature
    Purpose
    snapshot
    snapshot(live) → StateSnapshot
    Capture current state as a content-addressable blob
    diff
    diff(base, target) → StateDelta
    Compute minimal change between two snapshots (added · removed · modified)
    merge
    merge(base, left, right) → MergeResult
    Three-way reconcile divergent state lines; surface conflicts per dimension
    drift
    drift(committed, live) → DriftReport
    Detect uncommitted changes between HEAD and working state
    apply
    apply(delta, live) → LiveState
    Apply a delta during checkout to reconstruct historical state
    schema
    schema() → DomainSchema
    Declare data structure — drives diff algorithm selection per dimension
    Registry

    Registered Domains

    Domains currently registered in this Muse instance. The active domain is the one used when you run muse commit, muse diff, and all other commands.

    {{ACTIVE_DOMAINS}}
    Ecosystem

    The Plugin Ecosystem

    MIDI and code are the two shipped domains — both fully active with typed deltas, structured merge, and .museattributes rule control. These are the domains planned next — and the slot waiting for yours.

    {{PLANNED_DOMAINS}}
    Engine Capabilities

    What Every Plugin Gets for Free

    The core engine provides four advanced capabilities that any domain plugin can opt into. Implement the protocol — the engine does the rest.

    {{ICON_CODE}} Typed Delta Algebra StructuredDelta — every change is a typed operation

    Unlike Git's blob diffs, Muse deltas are typed objects: InsertOp, ReplaceOp, DeleteOp — each carrying the address, before/after hashes, and affected dimensions. Machine-readable with muse show --json.

    {{TYPED_DELTA_EXAMPLE}}
    {{ICON_LAYERS}} Domain Schema Per-domain dimensions drive diff algorithm selection

    Each plugin's schema() method declares its dimensions and merge mode. The engine uses this to select the right diff algorithm per dimension and to surface only the dimensions that actually conflict.

    {{ACTIVE_DOMAINS}}
    {{ICON_GIT_MERGE}} OT Merge Operational transformation — independent ops commute automatically

    Plugins implementing StructuredMergePlugin get operational transformation. Operations at different addresses commute automatically — only operations on the same address with incompatible intent surface a conflict.

    Scenario A Independent ops at different addresses
    left InsertOp "ot-notes-a.mid" tick=0 · C4 E4 G4
    right InsertOp "ot-notes-b.mid" tick=480 · D4 F4 A4
    transform → no overlap → ops commute {{ICON_CHECK_CIRCLE}} Clean merge · both files applied
    Scenario B Same address, conflicting intent — conflict surfaced
    left ReplaceOp "shared-melody.mid" C4 E4 G4 · major triad
    right ReplaceOp "shared-melody.mid" C4 Eb4 G4 · minor triad
    transform → same address · non-commuting content {{ICON_X_CIRCLE}} Conflict · human resolves
    {{ICON_ZAP}} CRDT Primitives Convergent merge — any two replicas always reach the same state

    Plugins implementing CRDTPlugin get six battle-tested convergent data structures. No coordination required between replicas. The MIDI plugin extends RGA into MidiRGA — a voice-aware variant that orders concurrent note insertions by voice lane (bass → tenor → alto → soprano) before falling back to op-id, preventing voice crossings without human intervention.

    {{CRDT_CARDS}}
    {{DIFF_ALGEBRA}}
    Build

    Build in Three Steps

    One command scaffolds the entire plugin skeleton. You fill in six methods. The full VCS follows.

    Step 1 · Scaffold
    Generate the skeleton
    One command creates the plugin directory, class, and all six method stubs with full type annotations.
    muse domains --new genomics
    Step 2 · Implement
    Fill in the six methods
    Replace each raise NotImplementedError with your domain's snapshot, diff, merge, drift, apply, and schema logic.
    vim muse/plugins/genomics/plugin.py
    Step 3 · Use
    Full VCS, instantly
    Register in registry.py, then every Muse command works for your domain out of the box.
    muse init --domain genomics
    The Scaffold

    What muse domains --new genomics produces

    A fully typed, immediately runnable plugin skeleton. Every method has the correct signature. You replace the stubs — the protocol does the rest.

    muse/plugins/genomics/plugin.py
    {{SCAFFOLD_SNIPPET}}

    Full walkthrough → docs/guide/plugin-authoring-guide.md · CRDT extension → docs/guide/crdt-reference.md

    Distribution

    How to Share Your Plugin

    Three tiers of distribution — from local prototype to globally searchable registry. Start local, publish when ready.

    {{DIST_CARDS}}

    MuseHub is coming

    A centralized, searchable registry for Muse domain plugins — think npm or crates.io, but for any multidimensional versioned state. One command to publish. One command to install.

    {{ICON_SEARCH}}
    Searchable
    Find plugins by domain, capability, or keyword
    {{ICON_PACKAGE}}
    Versioned
    Semantic versioning, pinned installs, changelogs
    {{ICON_LOCK}}
    Private registries
    Self-host for enterprise or research teams
    {{ICON_ZAP}}
    One command
    muse init --domain @musehub/genomics
    MuseHub — planned · building in public at github.com/cgcardona/musehub
    Muse v0.1.2 · domain-agnostic version control for multidimensional state · Python 3.14 Demo · GitHub · Plugin Guide
    """ # --------------------------------------------------------------------------- # Entry point # --------------------------------------------------------------------------- if __name__ == "__main__": import argparse parser = argparse.ArgumentParser( description="Generate the Muse domain registry HTML page" ) parser.add_argument( "--out", default=str(_ROOT / "artifacts" / "domain_registry.html"), help="Output HTML path", ) args = parser.parse_args() out_path = pathlib.Path(args.out) out_path.parent.mkdir(parents=True, exist_ok=True) print("Generating domain_registry.html...") render(out_path) print(f"Open: file://{out_path.resolve()}")