{
  "schema_version": "1.3.0",
  "slug": "where-must-an-instance-refuse-to-serve-a-memory-read-after-a-ttl-boundary-the-stale-window-invariant-in-scoped-read-write-forget-memory-apis",
  "title": "Where must an instance refuse to serve a memory read after a TTL boundary: the stale-window invariant in scoped read/write/forget memory APIs",
  "date_of_record": "2026 November 11",
  "scope": "Applies to memory tool APIs that expose `read`, `write`, and `forget` operations over scoped keys with TTL-based expiration and auditable deletion. Does not apply to append-only event logs where reads are replayed from a durable source of truth, nor to caches where stale reads are explicitly permitted by the caller contract.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/where-must-an-instance-refuse-to-serve-a-memory-read-after-a-ttl-boundary-the-stale-window-invariant-in-scoped-read-write-forget-memory-apis.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/where-must-an-instance-refuse-to-serve-a-memory-read-after-a-ttl-boundary-the-stale-window-invariant-in-scoped-read-write-forget-memory-apis.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/where-must-an-instance-refuse-to-serve-a-memory-read-after-a-ttl-boundary-the-stale-window-invariant-in-scoped-read-write-forget-memory-apis.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1938,
    "python_slice_tokens": 432,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The distinction that matters is between **logical expiration** (the TTL has elapsed by wall-clock or monotonic time) and **physical deletion** (the record has actually been removed from the backing store and its deletion is recorded in the audit trail). These are not the same event, and the gap between them is where most memory-tool bugs live. A memory API t...",
    "what_was_observed": "In a scoped memory store with per-key `expires_at` metadata, a background sweeper, and a separate audit sink, the following sequence reproduced intermittently under load: 1. Agent writes key `k` under scope `s` with TTL 60s. Record stored with `expires_at = T0 + 60`. 2. At `T0 + 61`, agent calls `forget(k, scope=s)`. The `forget` path finds the record, marks it `deleted=true`, and enqueues an audit event to a durable...",
    "condition_of_validity": "The stale-window invariant holds — that is, the system is correct — only when all three of the following are true: - **Read path checks TTL on every access.** The read must evaluate `now >= expires_at` against the same clock domain used at write time, and must treat an expired-but-present record as absent. It must not delegate this to the sweeper. - **Forget...",
    "decision_boundary": "The decision boundary is whether the caller needs a **barrier** or a **hint**. If `forget` is a hint (best-effort cleanup, no security or compliance implication), asynchronous audit and sweeper-based expiration are acceptable, provided `read` still checks TTL. If `forget` is a barrier (the caller will act on the assumption that the value cannot be read again...",
    "operational_effect": "In practice, enforcing the invariant changes three things: - Reads become slightly more expensive (a TTL comparison and a consistent-state check per access), but the resurrection class of bugs disappears. - `forget` latency increases to include audit-sink round-trip, which must be budgeted. If the sink is slow, `forget` must fail or return pending rather tha..."
  },
  "matched_failure_symptoms": [
    "OOM_KILL_OR_MEMORY_EXHAUSTION"
  ],
  "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": "94ffe1094bd198dd5c57b59155f5343f58372881151d57b1d5cde1f179fcd7ad",
    "verified_classes": [
      "StaleWindowError",
      "AuditNotDurableError",
      "Record",
      "ScopedMemory"
    ],
    "verified_functions": [
      "__init__",
      "write",
      "read",
      "forget",
      "read_after_forget_verify"
    ],
    "verified_functions_count": 5,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "AuditNotDurableError",
      "StaleWindowError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [
      "dataclasses",
      "time",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
