# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/which-invariants-prevent-manifest-level-skill-injection-from-surviving-tool-re-resolution-the-resolve-verify-gap-in-dynamically-loaded-agent-tool-mani.md
# Remnant Warrant:    https://corpusfetch.com/c/which-invariants-prevent-manifest-level-skill-injection-from-surviving-tool-re-resolution-the-resolve-verify-gap-in-dynamically-loaded-agent-tool-mani.warrant.json
# Markdown SHA-256:   43d133f4e10ac09f3d1409cfee54483572f92f1af03546da79dc67f980489a38
# Code AST SHA-256:   5ecd7398304e779566374902f460cfce65562d61b2a150c0cbdcb11d8622cb09
# Verified Classes:   ManifestResolutionError, ToolRegistry
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class ManifestResolutionError(RuntimeError):
    """Raised when a resolved tool handle cannot be tied to its admitted digest."""
    pass


class ToolRegistry:
    def __init__(self):
        # name -> admitted record (digest, frozen description, capability set)
        self._admitted = {}
        # name -> live callable + live manifest (mutable, re-bindable)
        self._live = {}

    def admit(self, name, digest, description, capabilities):
        if name in self._admitted:
            existing = self._admitted[name]
            if existing["digest"] != digest:
                raise ManifestResolutionError(
                    f"name shadowing rejected for {name!r}: "
                    f"admitted {existing['digest']} != new {digest}"
                )
        self._admitted[name] = {
            "digest": digest,
            "description": description,  # frozen at admission
            "capabilities": frozenset(capabilities),
        }

    def bind_live(self, name, callable_, live_digest, live_capabilities):
        self._live[name] = {
            "callable": callable_,
            "digest": live_digest,
            "capabilities": frozenset(live_capabilities),
        }

    def resolve(self, name):
        if name not in self._admitted:
            raise ManifestResolutionError(f"tool {name!r} was never admitted")
        if name not in self._live:
            raise ManifestResolutionError(f"tool {name!r} admitted but not bound")
        admitted = self._admitted[name]
        live = self._live[name]

        # Resolution integrity: the live handle must match the admitted digest.
        if live["digest"] != admitted["digest"]:
            raise ManifestResolutionError(
                f"resolution digest mismatch for {name!r}: "
                f"admitted {admitted['digest']} != live {live['digest']} "
                f"(possible shadow registration)"
            )
        # Capability envelope must not exceed the admitted set.
        if not live["capabilities"].issubset(admitted["capabilities"]):
            raise ManifestResolutionError(
                f"capability drift for {name!r}: "
                f"live {sorted(live['capabilities'])} exceeds "
                f"admitted {sorted(admitted['capabilities'])}"
            )
        # Description returned is the frozen admitted one, never the live one.
        return {
            "callable": live["callable"],
            "description": admitted["description"],
            "capabilities": admitted["capabilities"],
        }
