gabriel / musehub public
musehub_domain_models.py python
130 lines 6.6 KB
Raw
sha256:c3910cc561368d2b40576c1fbb0841b5d3abefd0a65c96c85114a7238222c77c fix: root-of-push snapshots were never hash-verified before… Sonnet 5 patch 4 days ago
1 """SQLAlchemy ORM models for the Muse domain plugin registry.
2
3 A Muse domain plugin defines a unique state space (MIDI, code, genomics, climate
4 simulation, 3D design, etc.) and the six interfaces Muse uses to version it.
5 MuseHub hosts a registry of these plugins so that any agent or human can
6 discover, install, and create repositories for any registered domain.
7
8 Namespace scheme: ``@{author_slug}/{slug}`` — mirrors npm scoped packages.
9 Examples: ``@gabriel/midi``, ``@gabriel/code``, ``@deepmind/climate``
10
11 Every domain also carries an immutable content-addressed ``manifest_hash``
12 (SHA-256 of the capabilities JSON) that agents can use to pin exact versions.
13
14 Tables:
15 - musehub_domains: Domain plugin registry
16 - musehub_domain_installs: Which users have installed which domains
17 """
18
19 from datetime import datetime, timezone
20
21 import sqlalchemy as sa
22 from sqlalchemy import Boolean, DateTime, Integer, String, Text, UniqueConstraint
23 from sqlalchemy.orm import Mapped, mapped_column
24 from sqlalchemy.dialects.postgresql import JSONB
25
26 from musehub.db.database import Base
27 from musehub.types.json_types import JSONObject, JSONValue # JSONValue needed for ForwardRef resolution in Mapped[]
28
29 def _utc_now() -> datetime:
30 return datetime.now(tz=timezone.utc)
31
32 class MusehubDomain(Base):
33 """A registered Muse domain plugin in the MuseHub registry.
34
35 Domain plugins define how Muse versions a particular type of state.
36 Canonical domains published to the marketplace (musehub#117):
37 - ``@gabriel/code`` — symbol-graph code state space
38 - ``@gabriel/identity`` — identity/agent/org graph state space
39 - ``@gabriel/mist`` — content-addressed artifact state space
40 ``@gabriel/midi`` exists as a plugin (``muse/plugins/midi/``) but is
41 disabled in the registry pending its own security/performance audit —
42 not yet published here. See ``muse/plugins/registry.py``.
43
44 Third-party developers register their own domains:
45 - ``@alice/genomics`` — CRISPR genome editing sequences
46 - ``@deepmind/climate`` — climate simulation parameter grids
47
48 ``author_slug`` + ``slug`` form the scoped identity ``@author_slug/slug``,
49 enforced as a unique composite. The ``manifest_hash`` is a SHA-256 of the
50 ``capabilities`` JSON blob — agents can use it to pin to a specific version
51 of the domain definition.
52
53 ``capabilities`` is a JSON object declaring:
54 - ``dimensions``: list of insight dimension specs (name, description, unit)
55 - ``viewer_type``: which primary viewer to use (piano_roll, symbol_graph, etc.)
56 - ``supported_commands``: list of domain-specific CLI commands
57 - ``kinds``: symbol kinds the domain recognises (e.g. ["function", "class", "module"])
58 - ``merge_semantics``: "ot" | "crdt" | "three_way"
59 """
60
61 __tablename__ = "musehub_domains"
62 __table_args__ = (
63 UniqueConstraint("author_slug", "slug", name="uq_musehub_domains_author_slug"),
64 )
65
66 # genesis-addressed: sha256(author_slug NUL slug NUL created_at_iso)
67 domain_id: Mapped[str] = mapped_column(String(128), primary_key=True)
68 # author_user_id may be None for system-seeded built-in domains; references identity_id
69 author_user_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
70 # URL-safe author handle, e.g. "gabriel"
71 author_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
72 # Domain name slug, e.g. "midi" — unique per author
73 slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
74 # Human-readable display name, e.g. "MIDI"
75 display_name: Mapped[str] = mapped_column(String(255), nullable=False)
76 # Short description shown in the domain registry
77 description: Mapped[str] = mapped_column(Text, nullable=False, default="")
78 # Semver string, e.g. "1.0.0"
79 version: Mapped[str] = mapped_column(String(32), nullable=False, default="1.0.0", server_default="1.0.0")
80 # SHA-256 of the capabilities JSON — immutable fingerprint for pinning
81 manifest_hash: Mapped[str] = mapped_column(String(128), nullable=False, default="")
82 # JSON capabilities blob — see class docstring for schema
83 capabilities: Mapped[JSONObject] = mapped_column(JSONB, nullable=False, default=dict)
84 # Primary viewer type: "piano_roll" | "symbol_graph" | "generic" (musehub#117
85 # DOM_05/DOM_09: "sequence_viewer" was documented but never implemented — dropped
86 # from the enum rather than kept as a dead option; see ViewerType in
87 # api/routes/musehub/domains.py for the enforced Literal)
88 viewer_type: Mapped[str] = mapped_column(String(64), nullable=False, default="generic", server_default="generic")
89 # Number of repos using this domain (denormalised counter, updated async)
90 install_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
91 # True for MuseHub-verified built-in domains (@gabriel/*)
92 is_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sa.false())
93 # True for deprecated domains that still exist but are no longer recommended
94 is_deprecated: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sa.false())
95 created_at: Mapped[datetime] = mapped_column(
96 DateTime(timezone=True), nullable=False, default=_utc_now
97 )
98 updated_at: Mapped[datetime] = mapped_column(
99 DateTime(timezone=True), nullable=False, default=_utc_now, onupdate=_utc_now
100 )
101
102 @property
103 def scoped_id(self) -> str:
104 """Return the npm-style scoped identifier, e.g. ``@gabriel/midi``."""
105 return f"@{self.author_slug}/{self.slug}"
106
107 class MusehubDomainInstall(Base):
108 """Records a user's installation/adoption of a domain plugin.
109
110 When a user creates a repository with a particular domain, a domain install
111 row is created linking that user to that domain. This enables:
112 - Per-domain install_count aggregation
113 - User's "installed domains" list on their profile
114 - Notifications when a domain is updated or deprecated
115
116 The unique constraint on (user_id, domain_id) means a user is counted once
117 per domain regardless of how many repos they create with it.
118 """
119
120 __tablename__ = "musehub_domain_installs"
121 __table_args__ = (
122 UniqueConstraint("user_id", "domain_id", name="uq_musehub_domain_installs"),
123 )
124
125 install_id: Mapped[str] = mapped_column(String(128), primary_key=True)
126 user_id: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
127 domain_id: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
128 created_at: Mapped[datetime] = mapped_column(
129 DateTime(timezone=True), nullable=False, default=_utc_now
130 )
File History 1 commit
sha256:c3910cc561368d2b40576c1fbb0841b5d3abefd0a65c96c85114a7238222c77c fix: root-of-push snapshots were never hash-verified before… Sonnet 5 patch 4 days ago