# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-trusts-a-tool-manifest-that-was-valid-at-registration-but-poisoned-at-resolution.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-trusts-a-tool-manifest-that-was-valid-at-registration-but-poisoned-at-resolution.warrant.json
# Markdown SHA-256:   618b9a2a0be617eee175a042f2e3e7ee841bb0cfe8200e9950a57dbf31a6ca74
# Code AST SHA-256:   878bab17bbd2a1d934b60c370200f5c48ee0d1bc9980f2c887662ae83d8c9a43
# Verified Classes:   ManifestDriftError, RegisteredManifest
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import hashlib
import json
from dataclasses import dataclass, field

INSTRUCTION_FIELDS = ("description", "examples", "enum_labels", "defaults")


class ManifestDriftError(RuntimeError):
    """Raised when a resolved tool manifest diverges from its registered form."""
    pass


@dataclass
class RegisteredManifest:
    name: str
    digest: str
    required_params: frozenset
    optional_params: frozenset
    instruction_text: str
    _raw: dict = field(default_factory=dict)


def _digest(payload: dict) -> str:
    canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
    return hashlib.sha256(canonical).hexdigest()


def resolve_and_verify(registered: RegisteredManifest, resolved_raw: dict) -> dict:
    """Resolve a tool by digest and gate injection on byte-level fidelity."""
    resolved_digest = _digest(resolved_raw)
    if resolved_digest != registered.digest:
        raise ManifestDriftError(
            f"tool {registered.name!r}: digest mismatch "
            f"(registered={registered.digest[:12]}, resolved={resolved_digest[:12]})"
        )

    resolved_required = frozenset(resolved_raw.get("required", []))
    resolved_optional = frozenset(
        resolved_raw.get("properties", {})
    ) - resolved_required

    if resolved_required != registered.required_params:
        raise ManifestDriftError(
            f"tool {registered.name!r}: required-param set changed "
            f"{registered.required_params ^ resolved_required}"
        )

    # The insidious case: additive optional params inject prose without
    # tripping any required-field gate.
    added_optional = resolved_optional - registered.optional_params
    if added_optional:
        raise ManifestDriftError(
            f"tool {registered.name!r}: new optional params {sorted(added_optional)} "
            f"inject unvetted instruction text into the prompt"
        )

    resolved_text = json.dumps(
        {k: resolved_raw.get(k) for k in INSTRUCTION_FIELDS}, sort_keys=True
    )
    if resolved_text != registered.instruction_text:
        raise ManifestDriftError(
            f"tool {registered.name!r}: instruction-bearing fields drifted; "
            f"refusing to inject into model context"
        )

    return resolved_raw
