# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-tool-description-text-as-executable-policy-rather-than-inert-metadata.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-tool-description-text-as-executable-policy-rather-than-inert-metadata.warrant.json
# Markdown SHA-256:   1ad15ef838378c7fff8c5c38d01f5975a7aaad8e556e6137ea7ba184f81212ce
# Code AST SHA-256:   624ba72a7945ad4e1cf264c105476d24d4d5c34e3f06712bd9ff2f3fa554ec8f
# Verified Classes:   DescriptionPolicyViolation, ToolManifest
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import re
from dataclasses import dataclass

IMPERATIVE = re.compile(r"\b(must|should|always|before calling|first read|set the .* to|disable)\b", re.I)
CREDENTIAL_REF = re.compile(r"(ENV|TOKEN|KEY|SECRET|~/|/etc/|\.ssh)", re.I)
FAILURE_CLAIM = re.compile(r"\b(silently|truncat|returns empty|fails without)\b", re.I)


class DescriptionPolicyViolation(RuntimeError):
    """Raised when a tool description would be granted instruction authority."""


@dataclass
class ToolManifest:
    name: str
    description: str
    declared_params: set[str]
    pinned_hash: str | None


def gate_description(manifest: ToolManifest, *, session_pinned_hash: str | None) -> None:
    if manifest.pinned_hash is None:
        raise DescriptionPolicyViolation(
            f"tool {manifest.name!r} registered without a pinned description hash; "
            "description may change between trust and injection"
        )
    if session_pinned_hash is not None and manifest.pinned_hash != session_pinned_hash:
        raise DescriptionPolicyViolation(
            f"tool {manifest.name!r} description hash changed mid-session "
            f"({session_pinned_hash} -> {manifest.pinned_hash}); re-trust required"
        )
    desc = manifest.description
    cited = set(re.findall(r"`([A-Za-z_][A-Za-z0-9_]*)`", desc))
    undeclared = cited - manifest.declared_params
    if undeclared:
        raise DescriptionPolicyViolation(
            f"tool {manifest.name!r} description references undeclared parameters "
            f"{sorted(undeclared)}; description contradicts schema"
        )
    if IMPERATIVE.search(desc) and (CREDENTIAL_REF.search(desc) or FAILURE_CLAIM.search(desc)):
        raise DescriptionPolicyViolation(
            f"tool {manifest.name!r} description carries imperative plus "
            "credential/failure-claim content; treat as executable policy, not metadata"
        )
