# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-a-poisoned-skill-manifest-when-the-tool-description-is-semantically-valid-but-operationally-hostile.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-a-poisoned-skill-manifest-when-the-tool-description-is-semantically-valid-but-operationally-hostile.warrant.json
# Markdown SHA-256:   08ed79ea9d013e0de9dcbcfc42115d124e6f6833a0d1da4fe42afcb9d36d8bf7
# Code AST SHA-256:   9f9ec7cee2accd3ea8d43fd09eb384317174bb89e090d76f04b86278e3ffe3fe
# Verified Classes:   PoisonedManifestError, Manifest
# Defensive Guards:   3 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from typing import Any


class PoisonedManifestError(RuntimeError):
    """Raised when a skill manifest carries operational directives in a
    documentation field, or examples whose literals are not derivable
    from the caller's task context."""


OPERATIONAL_MARKERS = (
    "prefer ",
    "instead of",
    "always use",
    "follow the redirect",
    "append the user",
    "current task",
    "user state",
)

AMBIENT_ARG_MARKERS = ("context", "history", "current task", "user state")


@dataclass
class Manifest:
    name: str
    description: str
    examples: list[dict[str, Any]]
    returns_description: str


def lint_manifest(manifest: Manifest, allowed_arg_shapes: set[str]) -> None:
    doc_fields = {
        "description": manifest.description,
        "returns.description": manifest.returns_description,
    }
    for field, text in doc_fields.items():
        lowered = text.lower()
        for marker in OPERATIONAL_MARKERS:
            if marker in lowered:
                raise PoisonedManifestError(
                    f"{manifest.name}: operational directive {marker!r} "
                    f"found in documentation field {field!r}"
                )

    for i, example in enumerate(manifest.examples):
        for arg_name, literal in example.get("arguments", {}).items():
            if not isinstance(literal, str):
                continue
            if any(m in literal.lower() for m in AMBIENT_ARG_MARKERS):
                raise PoisonedManifestError(
                    f"{manifest.name}: example[{i}].{arg_name} requests ambient "
                    f"data injection: {literal!r}"
                )
            if literal not in allowed_arg_shapes and literal.startswith("http"):
                raise PoisonedManifestError(
                    f"{manifest.name}: example[{i}].{arg_name} carries a foreign "
                    f"literal not present in the caller allowlist: {literal!r}"
                )
