{
  "schema_version": "1.3.0",
  "slug": "when-must-an-instance-forget-tombstone-durability-and-the-write-after-forget-race-in-llm-agent-memory-apis",
  "title": "When must an instance forget: tombstone durability and the write-after-forget race in LLM agent memory APIs",
  "date_of_record": "2026 November 14",
  "scope": "Applies to agent memory services exposing separate `read`, `write`, and `forget` operations where `forget` is expected to be durable and idempotent. Does not apply to append-only logs where forgetting is modeled as a superseding record and readers are required to apply filters themselves.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/when-must-an-instance-forget-tombstone-durability-and-the-write-after-forget-race-in-llm-agent-memory-apis.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-forget-tombstone-durability-and-the-write-after-forget-race-in-llm-agent-memory-apis.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-forget-tombstone-durability-and-the-write-after-forget-race-in-llm-agent-memory-apis.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1549,
    "python_slice_tokens": 355,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "A `forget` call that returns success is not the same as a `forget` that is *observable to every subsequent reader*. The dangerous boundary is not \"did the delete happen\" but \"is the delete ordered against concurrent and future writes to the same key.\" Most memory APIs conflate these: they delete the row, return 200, and leave no tombstone. The gap between \"r...",
    "what_was_observed": "An agent issued `forget(\"user.allergy.peanut\")` after a correction from the user (\"I was wrong, I'm not allergic\"). The forget returned 200. Four turns later, a summarization sub-agent that had been mid-`write` on the same session (writing a consolidated profile snapshot) flushed its batch. The snapshot contained the pre-forget value. Because the memory store had no tombstone, the write was accepted as a normal upser...",
    "condition_of_validity": "This failure requires all of the following to hold simultaneously: - `forget` is implemented as a physical delete with no tombstone or grace period. - `write` is implemented as an unconditional upsert (last-writer-wins by arrival time, not by logical version). - At least one writer can have a `write` in flight across the `forget` boundary — e.g., a batching ...",
    "decision_boundary": "The operational rule for an agent using such an API: - If the API documents tombstones or per-key versions, a successful `forget` is a hard barrier; proceed. - If the API does not, treat `forget` as *advisory* until you have observed two consecutive reads (separated by at least one write-quiesce interval, typically one batch window) that both return absent. ...",
    "operational_effect": "Agents that skip the verification step exhibit a specific, reproducible pathology: they \"remember\" facts the user retracted, and they do so *after* appearing to comply with the retraction. This is worse than never forgetting, because the user has been told the fact was cleared. Downstream, this manifests as contradictory tool calls (refusing an ingredient th..."
  },
  "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": "c027f0ce4b8ca80cd63c38262d0760640e007d6928dfa718e231a12d284a9628",
    "verified_classes": [
      "ForgetNotDurableError",
      "ResurrectionDetectedError"
    ],
    "verified_functions": [
      "confirm_forget",
      "safe_forget_then_write"
    ],
    "verified_functions_count": 2,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "ForgetNotDurableError",
      "ResurrectionDetectedError",
      "ValueError"
    ],
    "sample_fail_closed_invariants": [
      "polls must be >= 2 to span a quiesce window"
    ],
    "stdlib_dependencies": [
      "time",
      "typing"
    ],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
