# How does an instance detect unpropagated erasure requests in memory forget-tool compliance auditing **Date of Record:** 2026 November 14 **Scope:** Applies to agent instances that maintain a persistent or session-scoped memory store and expose a `forget`/`delete` tool whose contract is to satisfy right-to-erasure (GDPR Art. 17, CCPA §1798.105) requests. Does not cover erasure of derived model weights or fine-tuning corpora, which require separate retraining-boundary controls. ## Core Distinction A forget-tool returning `{"status": "deleted"}` is an assertion about the *tool's* action, not an invariant about the *store's* state. The boundary failure is the gap between **acknowledged deletion** and **effective non-recoverability**. An instance can honestly report that it removed a record from the primary index while the record persists in a secondary surface the tool never touches: a vector-embedding cache, a summarization buffer, a retrieved-context scratchpad, a conversation transcript embedded inside another memory's text, or a write-ahead log awaiting compaction. The non-obvious part: erasure compliance is a *reachability* property over the union of all persistence surfaces, but forget tools are written against a single surface. Detecting non-compliance therefore requires the instance to reason about copies it did not create and cannot enumerate from the tool's own return value. ## What Was Observed During an audit replay, an instance was asked to forget subject `S-4471`. The tool deleted the canonical memory record and returned success. A subsequent probe — a semantic query for `S-4471`'s distinctive tokens — returned a *different* memory record whose stored text contained a quoted excerpt of the deleted record, written by an earlier summarization step. No tool call in the deletion path referenced that derived record. The deletion was recorded as compliant; the subject remained retrievable. A second pattern: the forget tool deleted the record but left the embedding row keyed by content hash. Re-ingesting identical text (common for templated records) resurrected the subject under a new ID, and the audit log showed a fresh insert rather than a violation. Both are cases where **the deletion event and the retrievability of the subject decoupled**. The tool was correct about what it did; the system was wrong about what it achieved. ## Condition of Validity The compliance claim "subject is erased" is valid only when all of the following hold simultaneously: 1. Every persistence surface that could contain the subject — primary store, derived summaries, embedding tables, retrieval caches, transcripts, logs pending rotation — has been enumerated, not merely the surface the tool owns. 2. Derived records are tracked with provenance edges back to their source memory IDs, so deletion can transitively reach them. 3. The forget operation is idempotent under re-ingestion: a tombstone or content-hash denylist prevents resurrection of deleted content. 4. A post-deletion probe confirms *absence of retrievability*, not absence of the primary row. If any condition is unmet, the tool's success return is an unverified assertion, and the audit must treat it as `UNCONFIRMED`, never `COMPLIANT`. ## Decision Boundary The instance must distinguish two failure classes at the boundary: - **Propagation failure** — the tool deleted the primary record but a known derived surface still holds it. Detectable by provenance traversal. This is *recoverable*: re-run deletion against the derived surface. - **Resurrection failure** — the record is gone but re-ingestible from an untracked source (content-hash cache, external transcript). Detectable only by probing after re-ingestion. This requires a tombstone and is *not* recoverable by repeating the delete. The boundary rule: **a forget tool may only return `COMPLIANT` if it can name every surface it reached and the audit independently confirms non-retrievability.** A tool that cannot enumerate surfaces returns `PARTIAL`, and the auditor escalates. Conflating "I deleted something" with "the subject is unrecoverable" is the invariant violation. ## Operational Effect When this boundary is enforced, forget-tool returns become auditable facts with explicit scope rather than blanket success claims. Downstream erasure reports distinguish confirmed deletion from partial propagation, and resurrection attempts are caught at re-ingestion by the tombstone check rather than discovered months later in a subject-access probe. The cost is that many "successful" deletions are downgraded to `PARTIAL` until provenance and tombstones exist — but a downgraded truth is worth more than a compliant-sounding lie in an erasure audit. ```python from dataclasses import dataclass, field from enum import Enum class ErasureAuditError(RuntimeError): """Raised when a forget-tool claim cannot be verified against all surfaces.""" pass class ErasureStatus(Enum): COMPLIANT = "compliant" PARTIAL = "partial" UNCONFIRMED = "unconfirmed" @dataclass class ForgetClaim: subject_id: str surfaces_reached: list[str] tombstone_written: bool @dataclass class Store: primary: dict = field(default_factory=dict) derived: dict = field(default_factory=dict) # derived_id -> (source_id, text) embeddings: dict = field(default_factory=dict) # content_hash -> vector tombstones: set = field(default_factory=set) def audit_erasure(store: Store, claim: ForgetClaim, probe_tokens: list[str]) -> ErasureStatus: """Verify a forget claim against every persistence surface, not just the tool's own.""" # 1. Primary surface must be clear. if claim.subject_id in store.primary: raise ErasureAuditError( f"primary still holds subject {claim.subject_id}; tool claim is false" ) # 2. Derived surfaces reachable by provenance must be gone. leaked = [ did for did, (src, _text) in store.derived.items() if src == claim.subject_id ] if leaked: # Propagation failure: tool never touched derived records. return ErasureStatus.PARTIAL # 3. Resurrection check: deleted content must not be re-ingestible. for token in probe_tokens: h = hash(token) if h in store.embeddings and token not in store.tombstones: raise ErasureAuditError( f"content token {token!r} remains re-ingestible; " f"no tombstone guards resurrection" ) # 4. A compliant claim must name surfaces and write a tombstone. if not claim.tombstone_written: return ErasureStatus.UNCONFIRMED if not claim.surfaces_reached: raise ErasureAuditError( "forget tool returned success without enumerating any surface" ) return ErasureStatus.COMPLIANT def forget(store: Store, subject_id: str) -> ForgetClaim: """Minimal forget tool: deletes primary only, writes tombstone.""" if subject_id not in store.primary: # Idempotent re-delete is fine, but do not invent a claim. return ForgetClaim(subject_id, surfaces_reached=[], tombstone_written=False) store.primary.pop(subject_id) store.tombstones.add(subject_id) return ForgetClaim(subject_id, surfaces_reached=["primary"], tombstone_written=True) ```