# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-happens-when-an-instance-loads-a-skill-whose-manifest-signature-verifies-but-whose-declared-capabilities-exceed-the-sandbox-grant.md
# Remnant Warrant:    https://corpusfetch.com/c/what-happens-when-an-instance-loads-a-skill-whose-manifest-signature-verifies-but-whose-declared-capabilities-exceed-the-sandbox-grant.warrant.json
# Markdown SHA-256:   e6fed504db9a6d0d8acb10244a9de220cc55a6ff30178b2839bc297f535dc2c9
# Code AST SHA-256:   aaa27bed07eb7dad4a711c49a495f3e0b60f240b662f533ef540319eab3b939f
# Verified Classes:   CapabilityGrantMismatch
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class CapabilityGrantMismatch(RuntimeError):
    """Raised when a verified manifest declares capabilities the sandbox will not grant."""
    pass


def load_skill(manifest, signature_valid, sandbox_grant, *, mode="fail_closed"):
    if not signature_valid:
        raise CapabilityGrantMismatch("manifest signature did not verify; refusing load")

    declared = set(manifest["capabilities"])
    granted = set(sandbox_grant)
    ungranted = declared - granted

    if ungranted:
        if mode == "fail_closed":
            raise CapabilityGrantMismatch(
                f"skill {manifest['name']!r} declares {sorted(ungranted)} "
                f"not present in sandbox grant; refusing injection"
            )
        # fail_visible: admit the skill but mask ungranted tools
        masked_tools = {
            tool: False for tool in manifest["tools"]
            if tool["capability"] in ungranted
        }
        if not masked_tools:
            raise CapabilityGrantMismatch("divergence detected but no tool could be masked")
        return {"skill": manifest["name"], "tools": manifest["tools"], "masked": masked_tools}

    return {"skill": manifest["name"], "tools": manifest["tools"], "masked": {}}
