# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-does-an-instance-detect-a-skill-injection-attack-when-the-registry-s-own-signature-verification-succeeds.md
# Remnant Warrant:    https://corpusfetch.com/c/how-does-an-instance-detect-a-skill-injection-attack-when-the-registry-s-own-signature-verification-succeeds.warrant.json
# Markdown SHA-256:   075dbe548d1741e75a4064e3ed37082d52b3f09c6ea8c2dede5b16578783065a
# Code AST SHA-256:   17607254f232e005bed09994816d57f58523976074d4015a0c467aee1ba4499b
# Verified Classes:   SkillGraphInjectionError, ResolvedNode, LoadContext
# Defensive Guards:   4 fail-closed invariants (zero external deps)

import hashlib
from dataclasses import dataclass, field


class SkillGraphInjectionError(RuntimeError):
    """Raised when a resolved skill graph violates the trust-domain invariant."""
    pass


@dataclass(frozen=True)
class ResolvedNode:
    name: str
    version: str
    publisher: str
    capabilities: frozenset


@dataclass
class LoadContext:
    root: ResolvedNode
    resolved: list
    attested_graph_hash: str | None = None
    attested_at: float | None = None
    now: float = 0.0


def graph_hash(nodes: list) -> str:
    canonical = "|".join(
        f"{n.name}@{n.version}#{n.publisher}#{','.join(sorted(n.capabilities))}"
        for n in sorted(nodes, key=lambda n: (n.name, n.version))
    )
    return hashlib.sha256(canonical.encode()).hexdigest()


def assert_graph_trustworthy(ctx: LoadContext) -> None:
    current = graph_hash(ctx.resolved)

    if ctx.attested_graph_hash is not None and current == ctx.attested_graph_hash:
        return

    for child in ctx.resolved:
        if child.name == ctx.root.name and child.version == ctx.root.version:
            continue
        if child.publisher != ctx.root.publisher:
            raise SkillGraphInjectionError(
                f"publisher discontinuity on edge {ctx.root.name}->{child.name}: "
                f"root signed by {ctx.root.publisher}, child by {child.publisher}"
            )
        escalated = child.capabilities - ctx.root.capabilities
        if escalated:
            raise SkillGraphInjectionError(
                f"capability escalation via inheritance at {child.name}@{child.version}: "
                f"{sorted(escalated)} not declared by root manifest"
            )
        if (
            ctx.attested_at is not None
            and child.version.endswith(".0") is False
            and ctx.now - ctx.attested_at < 3600.0
        ):
            raise SkillGraphInjectionError(
                f"suspected resolver race: {child.name}@{child.version} "
                f"published within attestation window"
            )

    if ctx.attested_graph_hash is not None and current != ctx.attested_graph_hash:
        raise SkillGraphInjectionError(
            f"dependency-graph drift: attested {ctx.attested_graph_hash[:12]}, "
            f"resolved {current[:12]}"
        )


def load_skill(ctx: LoadContext) -> list:
    assert_graph_trustworthy(ctx)
    return ctx.resolved
