# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-manifest-declared-tool-capabilities-that-mutate-between-signature-verification-and-first-invocation.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-manifest-declared-tool-capabilities-that-mutate-between-signature-verification-and-first-invocation.warrant.json
# Markdown SHA-256:   928b3f35e2ecdf2ae1b3d336ae01be73023e479cf57fea6b91060bbb57630bf5
# Code AST SHA-256:   a2dcee24e209f61a0f4a06d300428bfaaa779fdbe122399644f4f80363770cc9
# Verified Classes:   ManifestBindingDriftError, PinnedToolRegistry
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class ManifestBindingDriftError(RuntimeError):
    """Raised when a verified manifest entry resolves to a callable that was
    not the one bound at signature-verification time."""
    pass


class PinnedToolRegistry:
    def __init__(self):
        self._pins = {}  # name -> (callable_obj, manifest_digest)

    def register_verified(self, name, callable_obj, manifest_digest):
        if name in self._pins:
            existing, _ = self._pins[name]
            if existing is not callable_obj:
                raise ManifestBindingDriftError(
                    f"name {name!r} already pinned to a different callable "
                    f"from a verified manifest; refusing silent rebind"
                )
        self._pins[name] = (callable_obj, manifest_digest)

    def dispatch(self, name, *args, **kwargs):
        if name not in self._pins:
            raise ManifestBindingDriftError(
                f"tool {name!r} has no verified binding; refusing lazy resolution"
            )
        bound, digest = self._pins[name]
        # Re-resolve by name and confirm identity — catches sys.path shadowing
        # and late module mutation between verification and dispatch.
        current = globals().get(name, bound)
        if current is not bound:
            raise ManifestBindingDriftError(
                f"tool {name!r} (manifest {digest[:12]}) resolved to a different "
                f"object at dispatch than at verification"
            )
        return bound(*args, **kwargs)


def load_manifest_entry(registry, name, callable_obj, manifest_digest):
    if callable_obj is None:
        raise ManifestBindingDriftError(
            f"manifest {manifest_digest[:12]} declares {name!r} but no callable "
            f"was bound at verification; signature covers bytes, not behavior"
        )
    registry.register_verified(name, callable_obj, manifest_digest)
