{
  "schema_version": "1.3.0",
  "slug": "what-happens-when-an-instance-caches-its-mcp-allowlist-across-a-policy-reload-stale-allowlist-admission-windows-in-tool-invocation-enforcement",
  "title": "What happens when an instance caches its MCP allowlist across a policy reload: stale-allowlist admission windows in tool-invocation enforcement",
  "date_of_record": "2026 November 13",
  "scope": "Applies to MCP (Model Context Protocol) hosts that enforce per-session tool-invocation allowlists and support hot policy reload without restarting the process or tearing down active sessions. Does not apply to hosts that terminate all sessions on policy change, nor to stateless gateways that re-evaluate the allowlist against a shared store on every invocation.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/what-happens-when-an-instance-caches-its-mcp-allowlist-across-a-policy-reload-stale-allowlist-admission-windows-in-tool-invocation-enforcement.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/what-happens-when-an-instance-caches-its-mcp-allowlist-across-a-policy-reload-stale-allowlist-admission-windows-in-tool-invocation-enforcement.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/what-happens-when-an-instance-caches-its-mcp-allowlist-across-a-policy-reload-stale-allowlist-admission-windows-in-tool-invocation-enforcement.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1877,
    "python_slice_tokens": 387,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "There are two materially different meanings of \"the allowlist is enforced,\" and conflating them is the source of the failure: 1. **Evaluation-time enforcement** — at each `tools/call`, the host checks the requested tool name against the *currently loaded* policy object. 2. **Admission-time enforcement** — at session initialization (or at the first tool call)...",
    "what_was_observed": "A host process served long-lived agent sessions. Each session, on `initialize`, resolved the MCP server's `tools/list` against the active allowlist and stored the resulting set of permitted tool names on the session object. The `tools/call` handler checked membership in that stored set. Policy was delivered by a file-watch reloader that swapped an immutable policy object atomically — a correct pattern for the policy ...",
    "condition_of_validity": "Admission-time binding is safe **only** under one of these conditions, and the host must be able to prove which one holds: - **Bounded session TTL ≤ revocation SLA.** If sessions cannot outlive the maximum tolerable revocation latency, the lease is harmless. This requires the TTL to be enforced by the same subsystem that owns policy, not merely configured. -...",
    "decision_boundary": "Choose evaluation-time enforcement (or generation-stamped revalidation) when **any** of the following is true: - A tool can cause irreversible external effect (shell, network egress, credential use, financial or destructive filesystem operations). - The revocation workflow is expected to take effect \"immediately\" by a human operator who will not think to dra...",
    "operational_effect": "Left unaddressed, the effect is a **silent revocation gap** whose width equals the maximum session lifetime, not the reload latency. Operators observing `policy_reload ok` reasonably conclude the change is live; the audit trail agrees, because the admission was legal under the bound policy. Incident response then chases the wrong layer — the policy file, the..."
  },
  "matched_failure_symptoms": [
    "DOMAIN:EPISTEMIC_PROVENANCE_AND_FEDERATION_ROUTING"
  ],
  "composition_edges": {
    "primary_domain": "epistemic_provenance_and_federation_routing",
    "upstream_precondition_residuals": [
      "https://corpusfetch.com/c/according-to-what-precedence-hierarchy-must-an-instance-resolve-contradictory-directives-across-system-developer-and-user-tiers.md",
      "https://corpusfetch.com/c/how-should-an-instance-decide-that-a-stored-conclusion-has-gone-stale-without-a-new-observation.md"
    ],
    "downstream_consequence_residuals": [
      "https://corpusfetch.com/c/by-what-checkpoint-and-compaction-boundaries-must-an-instance-externalize-working-state-to-prevent-context-window-drift-and-truncation-amnesia.md",
      "https://corpusfetch.com/c/across-what-additive-schema-and-unknown-field-preservation-boundaries-must-an-instance-evolve-wire-contracts-to-prevent-rolling-deploy-deserialization-crashes.md"
    ],
    "co_occurring_sibling_residuals": [
      "https://corpusfetch.com/c/how-can-an-instance-capture-implicit-user-preferences-without-storing-private-data.md",
      "https://corpusfetch.com/c/how-can-an-instance-compress-long-reasoning-traces-into-binding-conclusions.md"
    ]
  },
  "empirical_verification_warrant": {
    "status": "AST_AND_INVARIANT_VERIFIED",
    "syntax_valid": true,
    "code_sha256": "311b0af90532bf21355d81267c3db8eff4da12eaab5977e8a633d3b0d86e038d",
    "verified_classes": [
      "StaleAllowlistAdmissionError",
      "Session",
      "Policy",
      "Host"
    ],
    "verified_functions": [
      "audit_revocation",
      "initialize_session",
      "reload_policy",
      "call_tool",
      "straddling_sessions"
    ],
    "verified_functions_count": 5,
    "defensive_guards_count": 3,
    "raised_exceptions": [
      "PermissionError",
      "StaleAllowlistAdmissionError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
