#!/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 """ from __future__ import annotations import json import pathlib import subprocess import sys _ROOT = pathlib.Path(__file__).resolve().parent.parent # --------------------------------------------------------------------------- # Live domain data from the CLI # --------------------------------------------------------------------------- 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": "music", "active": "true", "capabilities": ["Typed Deltas", "Domain Schema", "OT Merge"], "schema": { "schema_version": "1", "merge_mode": "three_way", "description": "MIDI and audio file versioning with note-level diff and semantic merge", "dimensions": [ {"name": "melodic", "description": "Note pitches and durations over time"}, {"name": "harmonic", "description": "Chord progressions and key signatures"}, {"name": "dynamic", "description": "Velocity and expression curves"}, {"name": "structural", "description": "Track layout, time signatures, tempo map"}, ], }, } ] # --------------------------------------------------------------------------- # Scaffold template (shown in the "Build in 3 steps" section) # --------------------------------------------------------------------------- _SCAFFOLD_SNIPPET = """\ from __future__ import annotations 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 """ # --------------------------------------------------------------------------- # Planned / aspirational domains # --------------------------------------------------------------------------- _PLANNED_DOMAINS = [ { "name": "Genomics", "icon": "๐Ÿงฌ", "status": "planned", "tagline": "Version sequences, variants, and annotations", "dimensions": ["sequence", "variants", "annotations", "metadata"], "color": "#3fb950", }, { "name": "3D / Spatial", "icon": "๐ŸŒ", "status": "planned", "tagline": "Merge spatial fields, meshes, and simulation frames", "dimensions": ["geometry", "materials", "physics", "temporal"], "color": "#58a6ff", }, { "name": "Financial", "icon": "๐Ÿ“ˆ", "status": "planned", "tagline": "Track model versions, alpha signals, and risk state", "dimensions": ["signals", "positions", "risk", "parameters"], "color": "#f9a825", }, { "name": "Scientific Simulation", "icon": "โš›๏ธ", "status": "planned", "tagline": "Snapshot simulation state across timesteps and parameter spaces", "dimensions": ["state", "parameters", "observables", "checkpoints"], "color": "#ab47bc", }, { "name": "Your Domain", "icon": "โœฆ", "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": "๐Ÿ’ป", "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": "๐Ÿ“ฆ", "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": "๐ŸŒ", "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_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)") 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) 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) 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}") # --------------------------------------------------------------------------- # Large HTML template # --------------------------------------------------------------------------- _HTML_TEMPLATE = """\ Muse โ€” Version Anything ยท Domain Plugin Registry
    Muse ยท Domain Plugin Registry

    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.

    Build a Domain Plugin Watch the Demo โ†’
    ๐ŸŽต music ๐Ÿงฌ genomics ๐ŸŒ 3d-spatial ๐Ÿ“ˆ financial โš›๏ธ simulation ๐Ÿ”ฌ proteomics ๐Ÿ—๏ธ cad ๐ŸŽฎ game-state โœฆ your-domain ๐ŸŽต music ๐Ÿงฌ genomics ๐ŸŒ 3d-spatial ๐Ÿ“ˆ financial โš›๏ธ simulation ๐Ÿ”ฌ proteomics ๐Ÿ—๏ธ cad ๐ŸŽฎ game-state โœฆ your-domain
    The Contract

    The MuseDomainPlugin Protocol

    Every domain โ€” music, 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
    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

    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

    Music is the reference implementation. These are the domains planned next โ€” and the slot waiting for yours.

    {{PLANNED_DOMAINS}}
    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.

    ๐Ÿ”
    Searchable
    Find plugins by domain, capability, or keyword
    ๐Ÿ“ฆ
    Versioned
    Semantic versioning, pinned installs, changelogs
    ๐Ÿ”’
    Private registries
    Self-host for enterprise or research teams
    โšก
    One command
    muse init --domain @musehub/genomics
    MuseHub โ€” planned ยท building in public at github.com/cgcardona/muse
    """ # --------------------------------------------------------------------------- # 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()}")