{
  "schema_version": "1.3.0",
  "slug": "what-structural-signals-indicate-a-tombstone-has-outlived-its-ttl-witness-provenance-chains-deletion-audits-and-the-read-after-forget-invariant",
  "title": "What structural signals indicate a tombstone has outlived its TTL witness: provenance chains, deletion audits, and the read-after-forget invariant",
  "date_of_record": "2026 November 12",
  "scope": "Applies to memory tool APIs that combine per-record provenance metadata, TTL-based expiry, and audited deletion (forget) operations where a tombstone record must persist long enough to prove deletion occurred. Does not apply to systems where deletion is synchronous and total (no tombstone), nor to append-only stores where TTL is enforced only at read time with no separate deletion ledger.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/what-structural-signals-indicate-a-tombstone-has-outlived-its-ttl-witness-provenance-chains-deletion-audits-and-the-read-after-forget-invariant.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/what-structural-signals-indicate-a-tombstone-has-outlived-its-ttl-witness-provenance-chains-deletion-audits-and-the-read-after-forget-invariant.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/what-structural-signals-indicate-a-tombstone-has-outlived-its-ttl-witness-provenance-chains-deletion-audits-and-the-read-after-forget-invariant.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1116,
    "python_slice_tokens": 187,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The failure mode here is not \"TTL expired too early\" or \"deletion failed.\" It is the **decoupling of two clocks that are assumed to be one clock**: the *record TTL* (when the payload becomes unreadable) and the *witness TTL* (when the tombstone that attests the deletion becomes unreadable). In a correct implementation, the witness TTL must strictly dominate ...",
    "what_was_observed": "In a memory tool with `write(record, ttl=…)`, `read(id)`, and `forget(id, reason=…)`, the forget path wrote a tombstone as: The tombstone reused `record.ttl` (e.g., 3600s). The audit retention contract, however, promised 30 days of deletion provability. For 29 days and 23 hours after expiry, `read(id)` returned the correct `GONE` status with provenance. After the tombstone's own TTL elapsed, `read(id)` returned `NOT_...",
    "condition_of_validity": "The invariant `witness_ttl ≥ record_ttl + audit_retention` holds only when: 1. Tombstones are assigned a TTL from a *separate* policy object, never inherited from the forgotten record. 2. The provenance hash is stored in the tombstone, not merely referenced by it (a reference can dangle when the referent expires). 3. `audit(id)` and `read(id)` consult the sa...",
    "decision_boundary": "When `read(id)` returns `NOT_FOUND` for an id whose provenance chain was previously non-empty, the correct classification is **witness expiry**, not record absence. The decision is: - If `tombstone.ttl ≤ record.ttl` → reject the forget operation at write time (fail closed), do not emit a tombstone that cannot outlive its witness obligation. - If `audit(id)` ...",
    "operational_effect": "Conflating them produces a memory store that is *auditable in principle but not in practice*: deletion events evaporate on the same schedule as the data they deleted. Downstream agents that rely on `audit()` to satisfy compliance or to reconstruct causality will silently lose the deletion half of their history while retaining the write half. The fix is struc..."
  },
  "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": "ef69159f8588dfeab9cbc3cdedd6a3fe9faf005a8caa1689de29d63a7f24d666",
    "verified_classes": [
      "WitnessExpiryError"
    ],
    "verified_functions": [
      "issue_tombstone",
      "classify_absence"
    ],
    "verified_functions_count": 2,
    "defensive_guards_count": 2,
    "raised_exceptions": [
      "WitnessExpiryError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
