# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-skill-injection-viability-in-dynamically-loaded-agent-tool-registries.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-skill-injection-viability-in-dynamically-loaded-agent-tool-registries.warrant.json
# Markdown SHA-256:   19a960fc1076b1c253239abd7722cccb4fc46ebb37e82f65a709bb45ca8ee9f6
# Code AST SHA-256:   22f594454d0ec05be96f30e23ff18fe46f871e5a03f38e535a31ea5b9a5e0e7c
# Verified Classes:   SkillInjectionSurfaceError
# Defensive Guards:   4 fail-closed invariants (zero external deps)

class SkillInjectionSurfaceError(RuntimeError):
    """Raised when a manifest field influences dispatch without re-derivation."""
    pass


TRUSTED_FIELDS = {"description", "examples"}  # model-facing only


def validate_registration(manifest: dict, pinned_hash: str, derived_caps: set[str]) -> None:
    # 1. Declaration must not grant capability; it may only request it.
    requested = set(manifest.get("requires", []))
    if not requested.issubset(derived_caps):
        raise SkillInjectionSurfaceError(
            f"manifest requests {sorted(requested - derived_caps)} not derivable "
            f"from pinned entrypoint {pinned_hash}"
        )

    # 2. Any field outside the model-facing allowlist is structural; it must
    #    be hash-pinned, not trusted as declared text.
    for field in manifest:
        if field in TRUSTED_FIELDS:
            continue
        if field in ("module", "entrypoint", "defaults") and not manifest.get("_pinned"):
            raise SkillInjectionSurfaceError(
                f"structural field '{field}' is unpinned and consumed by dispatcher"
            )

    # 3. Name must not silently rebind an existing hash.
    existing = REGISTRY_BINDINGS.get(manifest["name"])
    if existing is not None and existing["hash"] != pinned_hash:
        raise SkillInjectionSurfaceError(
            f"rebinding '{manifest['name']}' from {existing['hash']} to {pinned_hash} "
            f"requires explicit version bump"
        )


def dispatch(tool_name: str, args: dict) -> dict:
    binding = REGISTRY_BINDINGS.get(tool_name)
    if binding is None:
        raise SkillInjectionSurfaceError(f"unknown tool '{tool_name}'")
    # Re-derive at dispatch; never merge manifest defaults here.
    return binding["callable"](**args)
