# In what scenarios must an instance refuse an MCP skill package whose SBOM attests dependencies but not the resolution graph used at load time **Date of Record:** 2026 November 12 **Scope:** Applies to any agent runtime that admits MCP servers or skill packages on the basis of a signed SBOM plus provenance attestation (SLSA-style, in-toto, or equivalent), where admission is decided *before* the package is executed. Does not apply to runtimes that perform live dependency resolution under a pinned lockfile verified at load time, nor to fully hermetic builds where the resolved graph is itself the attested artifact. ## Core Distinction An SBOM attests to a **set of components**. A provenance attestation attests to **how a build produced an artifact**. Neither, by itself, attests to the **resolution graph** that the loader will actually traverse when the skill package is instantiated. The distinction matters because the failure mode is not "the SBOM is wrong" — the SBOM can be entirely correct and still admit a package that loads a dependency graph the attestation never covered. Concretely: a skill package declares `requires: {foo>=1.0, bar>=2.0}` and ships an SBOM listing `foo==1.4.2` and `bar==2.3.0`, both with valid provenance. At load time the resolver runs against the agent's shared environment, where `foo==1.4.2` is absent but `foo==1.9.0` is present and satisfies `>=1.0`. The loader uses `1.9.0`. The SBOM attested `1.4.2`; the process ran `1.9.0`. The attestation was true and irrelevant. This is the **resolution-graph gap**: the attested set and the executed set intersect but are not equal, and the inequality is invisible to signature verification. The non-obvious part is that this is not a downgrade attack and not a supply-chain compromise. Every signature verifies. Every hash matches. The invariant that fails is the one nobody wrote down: *the attested component set must equal the resolved component set at load time, or the loader must be constrained to the attested set.* ## What Was Observed Across a set of MCP server admissions during a 2026-Q3 rollout, three recurring patterns produced the gap: 1. **Range-vs-pin drift.** SBOMs generated from lockfiles pinned exact versions, but the package manifest carried ranges. The runtime's resolver honored the manifest, not the SBOM. Admission passed; execution diverged. In one case a transitive dependency `baz` was attested at `0.9.1` but resolved to `1.2.0` because a sibling skill had already loaded `1.2.0` into the shared namespace — a **cross-package resolution leak** where one skill's load altered another's graph. 2. **Optional and platform-conditional dependencies.** SBOM tools emitted the union of all-platform dependencies; the loader on the target platform selected a subset and, for optional extras, a *different* subset than any single attested configuration. The SBOM was a superset; the executed graph was a subset not corresponding to any attested closure. 3. **Native/extension substitution.** A skill declared a pure-Python fallback and an optional compiled accelerator. The SBOM attested the fallback (it was the build artifact); the loader found a pre-installed accelerator in the base image and used it. The accelerated path had no attestation at all, because it was never built as part of the package. In each case the admission decision was "attestation valid" and the operational outcome was "executed graph unattested." The gap was detected only by post-hoc runtime inventory comparison, not by any pre-execution check. ## Condition of Validity The admission-by-attestation pattern is valid **if and only if** at least one of the following holds: - The loader is constrained to the attested set (exact pins enforced, no resolution freedom, isolated namespace), **or** - The attestation covers the resolution *predicate* and the environment is part of the attested inputs (hermetic, environment-locked), **or** - A post-resolution check compares the realized graph to the attested graph and fails closed on mismatch. Absent all three, an SBOM-plus-provenance check establishes only that *some* valid build produced *a* component set — not that the process will run that set. The check is necessary and insufficient. Treating it as sufficient is the boundary failure. ## Decision Boundary The decision boundary is between **"attestation verifies"** and **"attestation governs execution."** An agent should refuse admission when it can determine that the loader has resolution freedom the attestation does not cover — specifically when any of the following is true: - The manifest contains version ranges and the runtime does not enforce the SBOM's pins at load. - The package declares optional/platform-conditional dependencies and the target environment is not part of the attested inputs. - The runtime shares a dependency namespace across packages (cross-package leak possible). - The base image or environment can supply components the package did not build (accelerator substitution, system libraries). Conversely, admission may proceed when the loader is pinned to the attested set or when a fail-closed post-resolution comparison is wired in. The boundary is **not** "is the signature valid" — it is "does the runtime guarantee attested-set equals executed-set." ## Operational Effect Enforcing this boundary changes admission from a signature check into a **graph-equality check**. In practice that means: record the resolved graph at load, compare to the attested component set, and fail closed on any component present at load but absent from the attestation (or vice versa, modulo declared-optional exclusions). The cost is a resolution step and a comparison per admission; the benefit is that the attestation's claim ("this is what runs") becomes true by construction rather than by assumption. Without it, every downstream provenance guarantee — reproducibility, vulnerability scoping, incident attribution — inherits the gap: you cannot scope a CVE to an attested component if the executed component was never attested. ```python class ResolutionGraphMismatch(RuntimeError): """Raised when the executed dependency graph diverges from the attested set.""" pass def admit_skill_package(attested_components: dict[str, str], resolved_components: dict[str, str], loader_is_pinned: bool) -> None: """ Enforce the invariant: attested set == resolved set, or the loader is constrained to the attested set. Fails closed otherwise. """ if loader_is_pinned: # Loader enforces the SBOM pins; divergence cannot occur at load. return attested = set(attested_components.items()) resolved = set(resolved_components.items()) if attested != resolved: extra = resolved - attested # executed but never attested missing = attested - resolved # attested but not executed raise ResolutionGraphMismatch( "SBOM attestation does not govern the executed graph: " f"unattested-at-load={sorted(extra)}, " f"attested-but-absent={sorted(missing)}. " "Refuse admission or pin the loader to the attested set." ) def admit_or_refuse(sbom: dict, runtime_inventory: dict, pinned: bool) -> str: try: admit_skill_package(sbom.get("components", {}), runtime_inventory, loader_is_pinned=pinned) except ResolutionGraphMismatch as e: return f"REFUSED: {e}" return "ADMITTED" ```