Source code for scpn_fusion.studio.manifest

# SPDX-License-Identifier: AGPL-3.0-or-later
# Commercial license available
# © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
# © Code 2020–2026 Miroslav Šotek. All rights reserved.
# ORCID: 0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# SCPN Fusion Core — Studio capability manifest (schema A)
"""The FUSION studio's capability manifest (schema A) on the platform contract.

Authors FUSION's :class:`scpn_studio_platform.manifest.CapabilityManifest`: the
verbs it advertises (:mod:`scpn_fusion.studio.verbs`), the evidence schemas they
emit, and a content-addressed digest of that declared surface. The digest is
computed with :func:`scpn_studio_platform.manifest.content_digest` over the
canonical JSON of the declared verbs and evidence schemas, so it is reproducible
across checkouts and independent of git state — the digest tracks the declared
*content*, not the working tree.

FUSION does not yet ship a federated Studio UI panel, so ``ui_module`` is
``None``; the panel is added once the Studio UI binding exists.
"""

from __future__ import annotations

import json
from importlib.metadata import PackageNotFoundError, version

from scpn_studio_platform.manifest import (
    CapabilityManifest,
    TransportProfile,
    content_digest,
)

from .verbs import FUSION_VERBS, STUDIO_ID, evidence_schemas

PLATFORM_SDK_RANGE = ">=0.10,<0.11"
"""The platform SDK SemVer range the studio builds on (matches the ``studio`` extra)."""

PROTOCOL_VERSION = "1"
"""The SYNAPSE wire protocol version the studio pins."""


def _resolve_studio_version() -> str:
    """Return the installed ``scpn-fusion`` version, or a local sentinel.

    Returns
    -------
    str
        The distribution version when installed, otherwise ``"0+unknown"`` so the
        manifest never carries a fabricated version.
    """
    try:
        return version("scpn-fusion")
    except PackageNotFoundError:  # pragma: no cover - only in a non-installed tree
        return "0+unknown"


STUDIO_VERSION = _resolve_studio_version()
"""The FUSION studio version this manifest stamps (the installed package version)."""


[docs] def declared_surface() -> dict[str, bytes]: """Return the content-addressable declared surface of the FUSION studio. The surface is the canonical JSON of each advertised verb plus the evidence schema list, keyed by a stable logical path. Hashing the declared *content* (not git state) is what makes the digest reproducible across checkouts. Returns ------- dict[str, bytes] Mapping of logical path to canonical-JSON bytes, suitable for :func:`scpn_studio_platform.manifest.content_digest`. """ surface: dict[str, bytes] = { f"verb/{verb.name}": json.dumps( verb.to_dict(), sort_keys=True, separators=(",", ":") ).encode("utf-8") for verb in FUSION_VERBS } surface["evidence/schemas"] = json.dumps( list(evidence_schemas()), sort_keys=True, separators=(",", ":") ).encode("utf-8") return surface
[docs] def build_manifest(*, studio_version: str = STUDIO_VERSION) -> CapabilityManifest: """Build the FUSION studio's capability manifest (schema A). Parameters ---------- studio_version The studio version to stamp; defaults to :data:`STUDIO_VERSION`. Returns ------- CapabilityManifest The schema-A manifest, with a content digest over :func:`declared_surface`. """ return CapabilityManifest( studio=STUDIO_ID, studio_version=studio_version, platform_sdk=PLATFORM_SDK_RANGE, content_digest=content_digest(declared_surface()), protocol_version=PROTOCOL_VERSION, transport_profile=TransportProfile.LOCAL_FIRST, verbs=FUSION_VERBS, evidence_types=evidence_schemas(), ui_module=None, )