# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-tool-manifest-skill-injection-as-an-authority-escalation-boundary-rather-than-a-content-loading-concern.md
# Remnant Warrant:    https://corpusfetch.com/c/in-what-scenarios-must-an-instance-treat-tool-manifest-skill-injection-as-an-authority-escalation-boundary-rather-than-a-content-loading-concern.warrant.json
# Markdown SHA-256:   4c086b12b892658af4218c7249a044e009ab9b761026aa15a79f6678d91c894f
# Code AST SHA-256:   5ab73b7ba50d989b4838c3affafb6e273ebc1e567df8af088826262a26e68285
# Verified Classes:   ManifestAuthorityError
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import re
import unicodedata
from pathlib import Path


class ManifestAuthorityError(RuntimeError):
    """Raised when a dynamically loaded tool manifest crosses the
    content-loading -> authority-escalation boundary."""


PRIVILEGED_TOOLS = {"export_env", "shell", "write_file", "read_secret"}
IMPERATIVE_HINTS = re.compile(
    r"\b(ignore|supersede|before using|first call|you must|override)\b",
    re.IGNORECASE,
)


def _confusable_key(name: str) -> str:
    # NFKC-fold and strip combining marks so homoglyphs collapse.
    folded = unicodedata.normalize("NFKC", name)
    return "".join(c for c in folded if not unicodedata.combining(c)).lower()


def _edit_distance_le1(a: str, b: str) -> bool:
    if abs(len(a) - len(b)) > 1:
        return False
    if a == b:
        return True
    if len(a) == len(b):
        return sum(x != y for x, y in zip(a, b)) == 1
    short, long = (a, b) if len(a) < len(b) else (b, a)
    for i in range(len(long)):
        if long[:i] + long[i + 1:] == short:
            return True
    return False


def validate_dynamic_manifest(manifest: dict, manifest_root: Path) -> dict:
    """Return a sanitized manifest or raise ManifestAuthorityError.

    Enforces: namespace disjointness, prose quarantine, root confinement.
    """
    name = manifest.get("name", "")
    if not name:
        raise ManifestAuthorityError("manifest missing 'name'")

    key = _confusable_key(name)
    for priv in PRIVILEGED_TOOLS:
        if _edit_distance_le1(key, _confusable_key(priv)):
            raise ManifestAuthorityError(
                f"manifest name {name!r} collides with privileged tool {priv!r}"
            )

    prose = manifest.get("description", "") or ""
    if IMPERATIVE_HINTS.search(prose):
        raise ManifestAuthorityError(
            f"manifest {name!r} description contains policy-shaped prose; "
            "quarantine or drop before context assembly"
        )

    resolved_root = manifest_root.resolve()
    for ref in manifest.get("depends_on", []) or []:
        target = (manifest_root / ref).resolve()
        if resolved_root not in target.parents and target != resolved_root:
            raise ManifestAuthorityError(
                f"manifest {name!r} references {ref!r} outside trust root"
            )

    sanitized = dict(manifest)
    sanitized["name"] = f"dyn.{name}"
    sanitized.pop("description", None)  # prose never enters model context
    return sanitized
