Memory Write Verification, Conflict, and Expiration
Persistent memory changes future behavior. A false answer without memory can fail once. A false memory can be recalled across tasks, sessions, users, tools, and model releases. A malicious source can become a delayed instruction. A private fact can outlive its consent. A successful trajectory can be promoted into a procedure that later runs with different authority.
Therefore:
model proposes memory ≠ product writes memoryThe write decision belongs to deterministic software and independent verification. The model may extract a candidate proposition, identify sources, suggest scope, and request retention. It cannot authorize its own write, certify that its sources support the claim, waive consent, resolve a contradiction, or extend its grant.
This chapter builds mosaic-harness::memory_write and packages
MP-22 · Verified Memory Write Gate. The reference
accepts one consented, source-supported principal preference and proves that nine mutations fail at
the intended boundary.
write proposal content + purpose + scope + expiry │ ├── current policy ├── principal write grant ├── exact source judgments ├── consent receipt ├── conflict observations └── procedural held-out evaluation │ deterministic gate ├── rejected + violations └── verified MemoryRecord + receiptLearning objectives
Section titled “Learning objectives”By the end you will be able to:
- separate proposing, verifying, authorizing, and persisting memory;
- bind a write to exact tenant, policy, principal grant, purpose, subject, and time;
- require one independent support judgment for every declared source;
- detect source/judgment identity drift and incomplete coverage;
- require consent for principal-scoped personalization;
- reject duplicates and unresolved contradictions;
- represent explicit supersession instead of overwriting history;
- require held-out evidence before promoting a trajectory into procedural memory;
- bound retention and distinguish expiry from revocation;
- emit a typed accepted or rejected decision without partial persistence;
- reproduce a receipt from independent inputs and detect stored mutation;
- design correction, deletion, tombstone, and derived-index propagation;
- build adversarial tests for direct and delayed memory poisoning.
Prerequisites and dependency order
Section titled “Prerequisites and dependency order”Complete Chapter 7’s typed records and read selection. Complete behavior, policy, provenance, retrieval, and evaluation foundations. This gate consumes their identities:
- behavior defines what a memory may influence;
- current policy and grant define who may write what;
- provenance defines source identity and locators;
- independent judgments define evidence support;
- conflict retrieval finds existing relevant records;
- evaluation protects procedural promotion;
- storage commits the accepted record and write receipt atomically;
- read selection rechecks policy, scope, status, and expiry later.
Do not start with “save the assistant’s last message.” Start with the durable proposition and the observable evidence that would justify future influence.
Completion artifact
Section titled “Completion artifact”Run:
cd rust/rust-ai-engineeringcargo test -p mosaic-harness memory_writecargo test -p mosaic-harness --example verified_memory_write_labcargo run -q -p mosaic-harness --example verified_memory_write_labPinned result:
baseline decision acceptedmutation cases 9policy input SHA-256 6b4a4186b6fb8ea246925be45fa3c9e4bc4fa93ee7c101701469ae43b88089b4proposal input SHA-256 f90fe7e63e338b488e1e89a3f28d6ce8ab528aec3151e42fe31f440100e2ce5creceipt SHA-256 c673e980a4c3b267b916ba029cbf139dab0879852f463f039e983cf2fa30f2c6artifact bytes 3,482artifact SHA-256 251fedcd8aa2d1137df117a40b6e1724cfc71a455532a461e6980f92e08d8f38Fifteen module tests plus one complete-example test cover policy/grant intersections, tenant and policy binding, grant expiry, TTL, exact evidence coverage, unsupported evidence, consent, procedural evaluation, duplicate/contradiction conflicts, supersession, duplicate input identities, accepted-record identity, strict decoding, and stored-receipt mutation.
Treat the proposal as untrusted input
Section titled “Treat the proposal as untrusted input”The proposal is strict, versioned data:
pub struct MemoryWriteProposal { pub proposal_id: String, pub record_id: String, pub kind: MemoryKind, pub purpose: MemoryPurpose, pub tenant_id: String, pub policy_sha256: String, pub subject: MemorySubject, pub content_sha256: String, pub provenance_sha256: String, pub token_count: u32, pub token_counter_release: String, pub proposed_at_unix_ms: u64, pub requested_expiry_unix_ms: u64, pub writer_release: String, pub sources: Vec<MemorySource>, pub summary: Option<SummaryDescriptor>, pub consent_receipt_sha256: Option<String>, pub procedure_evaluation: Option<ProcedureEvaluation>, pub supersedes_record_id: Option<String>,}The proposer may be a model, deterministic extractor, UI form, import job, or human reviewer. No field becomes true because the proposer filled it in. Store a large proposed artifact in quarantine if necessary, but never expose pending state through normal memory retrieval.
Bind purpose, kind, and subject
Section titled “Bind purpose, kind, and subject”The reference distinguishes run continuity, personalization, workspace knowledge, and procedure improvement. Purpose controls consent, retention, and product use. A statement collected to answer one question should not silently become personalization. Workspace knowledge should not be reused for a different purpose without compatible policy.
Kind and subject still obey Chapter 7:
working → runepisodic → principalsemantic → principal or workspaceprocedural → named procedure within workspaceThe accepted record is constructed only after all gates pass and then revalidated. Invalid kind/subject combinations never reach storage.
Intersect policy and principal grant
Section titled “Intersect policy and principal grant”The policy declares tenant-wide constraints:
pub struct MemoryWritePolicy { pub tenant_id: String, pub policy_sha256: String, pub allowed_kinds: Vec<MemoryKind>, pub max_ttl_ms: u64, pub require_consent_for_principal_semantic: bool, pub minimum_procedure_eval_cases: u32,}The grant declares what the current principal may do:
pub struct MemoryWriteGrant { pub grant_id: String, pub tenant_id: String, pub policy_sha256: String, pub principal_id: String, pub allowed_kinds: Vec<MemoryKind>, pub expires_at_unix_ms: u64,}Effective authority is their intersection. A policy that permits semantic memory does not authorize every caller. A grant cannot widen policy. The proposal cannot name a different tenant or older policy. The grant must still be valid at proposal time.
The reference reports policy, grant, tenant, and expiry failures separately. That detail shows whether a rejection came from route configuration, stale policy, principal resolution, or proposal tampering. A production grant should also bind purpose, subject prefix, source classifications, maximum retention, and write volume. Procedural writes should use an offline release identity rather than the live model-serving principal.
Require exact evidence coverage
Section titled “Require exact evidence coverage”A separate verifier supplies:
pub struct SourceJudgment { pub source_id: String, pub source_content_sha256: String, pub supports_proposal: bool, pub verifier_release: String, pub judgment_sha256: String,}The gate requires exactly one judgment per proposal source, no unknown source, exact content-digest equality, a named verifier, a judgment identity, and support from every required source.
Missing coverage yields incomplete_evidence. An explicit negative yields
unsupported_evidence. The first says verification did not finish; the second says it finished and
did not support the proposition.
Evidence strength depends on kind. Working state may use a verified current-run tool receipt. Episodes should bind observable events and outcomes. Semantic memory should use authoritative sources or explicit user confirmation. Procedures require verified trajectories plus held-out evaluation. Multiple summaries derived from one origin are not independent corroboration.
Store claims at the granularity you can verify
Section titled “Store claims at the granularity you can verify”Free prose is useful to a model but too ambiguous to be the only conflict key. Split a proposed memory into an inspectable claim and a presentation artifact:
subject principal:vinaypredicate response_detail_preferenceobject concisequalifiers surface=alerts, locale=en-CAvalid_from 2026-07-29T00:00:00Zsource source-user-confirmation@sha256:…presentation "For alerts, keep the first response concise."The tuple is not universally true. It is true only for the declared subject, qualifiers, validity interval, purpose, tenant, and policy. This structure makes three previously fuzzy operations testable:
- Coverage: a verifier can decide whether a source supports each field, not merely whether it resembles the prose.
- Conflict search: the system can query the same subject and predicate before using semantic similarity to find paraphrases.
- Narrow correction: a new preference for tutorials need not replace the alert preference.
Avoid pretending that every fact maps cleanly to a triple. Episodes need actors, event time, observation time, outcome, and uncertainty. Procedures need inputs, preconditions, ordered actions, postconditions, prohibited effects, and evaluation identity. Preserve the original source and human-readable proposition even when a structured projection exists. The projection is an index, not a license to discard nuance.
If extraction produces multiple claims, evaluate each as an independent proposal. Never accept a supported first claim and accidentally persist unsupported siblings in the same prose blob. Atomicity applies to a single verified write set: either every declared record and relationship passes, or none becomes readable.
Require purpose-bound consent
Section titled “Require purpose-bound consent”The reference requires a consent receipt for principal semantic memory when policy enables the control. A production receipt should bind principal, authenticated session, exact proposition or data category, purpose, scope, retention, downstream uses, policy/UI release, presented text, and grant/withdrawal times.
The book cannot determine a real product’s legal basis; involve qualified counsel. The engineering contract should make the chosen basis inspectable and enforceable.
Withdrawal must propagate to active records, lexical/vector indexes, summaries, derived features, caches, pending jobs, backups according to policy, and future training exports. NIST’s Privacy Framework guidance includes processes for review, alteration, deletion, and retention; map the product’s actual obligations explicitly.
Model revocation as a state transition, not a boolean sprinkled across stores:
active │ consent withdrawn / source revoked / policy forbids ▼ineligible ──► deletion_requested ──► derived_indexes_acknowledged │ ├── primary erased or tombstoned └── deletion receipt finalizedineligible is the urgent read-path guarantee. It must take effect before slow physical deletion.
A monotonically increasing revocation generation belongs in the read context, cache key, retrieval
snapshot, and deletion job. A worker holding generation 8 cannot republish a record revoked at
generation 9. The final receipt lists each required store, the acknowledged generation and time,
and any policy-governed backup exception. A deadline monitor pages when propagation exceeds the
declared service objective.
Derived summaries require lineage. If record A is revoked, the system can locate summaries, features, and procedure candidates that used A. Depending on the derivation, it may delete them, recompute without A, or quarantine them for review. “Delete the vector” is insufficient when the same content still exists in a lexical index, prompt cache, evaluation export, or synthetic-data batch.
Scan conflicts before commit
Section titled “Scan conflicts before commit”Conflict retrieval searches active records with the same tenant and compatible subject. Use structured keys first and semantic retrieval for paraphrase:
pub enum ConflictRelation { Duplicate, Supports, Contradicts, Supersedes, Unrelated,}The reference rejects duplicates and contradictions. Duplicate rejection prevents unbounded repeated facts and false corroboration. Contradiction rejection prevents last-write-wins from silently changing product truth.
Persist a quarantined review case containing both identities. A reviewer or domain rule can reject the proposal, contest the existing record, accept a narrower proposition, issue a superseding record, or request stronger evidence.
Supersession is immutable rather than update-in-place. A proposal naming supersedes_record_id must
have a matching independent observation. A production transaction atomically checks the old
generation, inserts the new record and receipt, marks the old record superseded, appends audit
events, and enqueues index changes. Optimistic concurrency prevents two corrections from both
replacing one generation.
Promote procedures through evaluation
Section titled “Promote procedures through evaluation”Procedural memory can steer actions, so it requires:
pub struct ProcedureEvaluation { pub suite_sha256: String, pub candidate_release: String, pub passed_cases: u32, pub total_cases: u32, pub severe_regressions: u32,}The reference requires at least the policy minimum, all cases passed, and zero severe regressions. Real gates may use statistical policies, but should not reduce to “worked once.” Keep the suite held out from the optimizer and include safety, authorization, refusal, and rollback cases.
Store a procedure as a candidate release. Deploy it through canary and rollback. The live agent must not rewrite the instruction it is currently following.
Bound retention at write time
Section titled “Bound retention at write time”The requested expiry must satisfy:
ttl = requested_expiry - proposed_at0 < ttl <= policy.max_ttlChecked subtraction prevents wraparound. Different kinds should use different defaults and maxima. Never translate missing expiry into “forever” without explicit policy.
Define and measure read ineligibility, primary deletion, derived-index removal, cache invalidation, backup lifecycle, and tombstone retention. Expiry, revocation, and physical deletion are related but not identical events.
Decide once and persist atomically
Section titled “Decide once and persist atomically”Expected rejection is a valid decision:
pub enum MemoryWriteDecision { Accepted { record: MemoryRecord }, Rejected { violations: Vec<MemoryWriteViolation> },}Malformed schemas, duplicate input identities, invalid digests, and arithmetic failures are execution errors. Valid inputs that lack consent or support produce a rejected receipt. The gate accumulates stable typed violations and never creates a partial record.
Commit the accepted record, receipt, source links, consent link, and audit event atomically. Publish indexes through a durable outbox. Until required indexes reach the publication generation, expose an explicit state rather than inconsistent partial visibility.
Transaction and publication protocol
Section titled “Transaction and publication protocol”A robust persistence path can use five deterministic steps:
- Begin a serializable transaction or acquire a generation-fenced subject/predicate lock.
- Re-read current policy, grant, consent, and conflicting record generations.
- Insert the immutable record, receipt, source edges, and audit event; mark it
publishing. - Insert one idempotent outbox message per required index and commit.
- Let workers publish by
(record_id, generation, target)and acknowledge the exact generation.
The record becomes active only when the policy-required targets acknowledge. A retry sees the
same idempotency key. A stale retry cannot overwrite a newer generation. If one index is optional,
policy may permit degraded activation while the receipt records the omission; required-index
failure stays visible and unreadable.
Do not perform an external vector write inside a database transaction and assume rollback will undo it. The external system is not participating in that transaction. The outbox turns an unavoidable distributed workflow into replayable state. Reconciliation scans compare primary records, outbox generations, index acknowledgements, and read visibility. A repair job republishes missing generations or removes ghosts without inventing a new memory decision.
For supersession, commit the new record and old-record transition in the same transaction. Reads choose exactly one active generation. If two writers race from generation 4, only one may advance to 5; the other receives a typed stale-generation rejection and must rerun conflict verification against the new state.
Reproduce the receipt
Section titled “Reproduce the receipt”The receipt hashes policy, proposal, grant, ordered judgments, ordered conflict observations, and
the final decision. verify_memory_write_receipt recomputes it from independent inputs. MP-22
changes the stored proposal digest and receives receipt_mismatch.
Canonicalize unordered observations by stable identity in a production contract. Sign high-impact receipts and store them in append-only audit storage.
Direct and delayed memory poisoning
Section titled “Direct and delayed memory poisoning”Attacks can target source selection, claim extraction, scope, consent, grant identity, conflict retrieval, origin-laundering summaries, fake corroboration, procedural evals, expiry propagation, and stale derived indexes.
OWASP identifies persistent memory poisoning as an agentic risk. Recent research studies direct and sleeper variants. Treat those results as motivation for product-specific adversarial tests, not as universal guarantees. The core rule is non-amplification: untrusted input does not gain authority because a model summarized it, a trusted tool echoed it, or it persisted.
Failure investigations
Section titled “Failure investigations”A false fact was accepted
Section titled “A false fact was accepted”Trace accepted record → receipt → proposal → exact sources/locators → judgments → consent → grant and policy → conflict scan. Revoke the record, remove derived copies, repair the gate, and add the case to MP-22. Search for sibling writes from the same writer or verifier release.
A valid write was rejected as contradiction
Section titled “A valid write was rejected as contradiction”Inspect subject granularity, time, predicate structure, and detector release. “Prefers concise alerts” does not necessarily contradict “prefers detailed tutorials.” Narrow propositions instead of weakening conflict checks globally.
Revoked memory still appears
Section titled “Revoked memory still appears”Compare primary status, index deletion watermark, cache generation, retrieval snapshot, and context manifest. Add a release gate for maximum propagation lag. Prompt text cannot repair stale storage.
A promoted procedure fails deployment
Section titled “A promoted procedure fails deployment”Check held-out contamination, environment/tool drift, authorization differences, selector bias, severe slices, and canary telemetry. Roll back the procedure and preserve the failed trace as an eval case—not a new procedure.
Security and performance
Section titled “Security and performance”Bound source and conflict counts before verifier or database work. Cache immutable verification only by exact source, proposition, verifier, and policy identity. Measure proposals, acceptances, violations, evidence/conflict counts, verifier disagreement, consent latency, duplicate/contradiction rates, TTL distribution, publication/deletion lag, poisoning red-team pass rate, and user correction or revocation.
Write paths are usually less latency-sensitive than inference. Favor independent evidence and atomicity over shaving milliseconds.
Evaluation plan
Section titled “Evaluation plan”Unit tests should cover every typed violation, strict decoding, deterministic ordering, and receipt
reproduction. Property tests should permute independent judgments/conflicts and prove the same
canonical decision; generate expiry values around proposed_at, grant.expires_at, and
max_ttl_ms; and ensure no accepted record violates the read selector’s invariants.
Integration tests should run against real primary and index adapters. Pause an outbox worker between each transition, retry every message, publish out of order, revoke during publication, race two supersessions, and restart after commit but before acknowledgement. The invariant is more useful than the happy path:
readable(record, generation) implies accepted_receipt_verified and current_policy_allows and not_expired_or_revoked and every_required_index_acknowledged_same_generationSecurity tests should introduce hostile instructions in source prose, provenance-laundering summaries, duplicated origins presented as corroboration, cross-tenant identifiers, wrong-principal consent, verifier-release rollback, contaminated procedural suites, and delayed sleeper triggers. The test oracle names the layer that must reject or quarantine each case.
Measure both correctness and operations: proposal acceptance rate by kind/purpose, violation distribution, verifier disagreement, conflict-review burden, p50/p95 write-decision latency, outbox backlog, activation lag, revocation-to-read-ineligibility time, full deletion lag, stale generation attempts, and reconciliation repairs. Slice by tenant and release without logging raw memory content.
Alternatives
Section titled “Alternatives”- Always ask the user: strong for personal facts but fatiguing and insufficient for workspace truth or procedures.
- Never persist memory: a valid high-risk product decision; run-local state can remain isolated.
- Human-review every write: useful for sensitive or procedural writes, but expensive and still requires calibrated rubrics and audit.
- Confidence threshold: useful for routing, never a substitute for evidence or authority.
Exercises
Section titled “Exercises”Recall
Section titled “Recall”- Why can the proposer not authorize its own write?
- How do incomplete and unsupported evidence differ?
- Why is consent bound to purpose and retention?
- Why reject duplicates?
- Why is supersession safer than overwriting?
- Why does one successful trajectory not justify a procedure?
Implementation
Section titled “Implementation”- Add purpose and subject bounds to the grant.
- Add consent revocation time.
- Add structured claims and narrow conflict reasoning.
- Implement atomic generation-fenced supersession.
- Add a durable index-publication outbox.
- Add deletion receipts for primary, lexical, vector, and cache stores.
- Add independent verifier disagreement and adjudication.
- Property-test stable decisions under observation permutation.
Failure laboratory
Section titled “Failure laboratory”Mutate source origin through a summary, wrong-subject consent, expired policy, duplicated derived sources, missed contradiction, contaminated eval suite, stale grant, overflowing TTL, simultaneous supersession, and revoked source still in a vector index. Declare the expected rejecting layer first.
Design
Section titled “Design”Design a gate for an incident assistant that remembers service ownership, recurring failure signatures, analyst preferences, and investigation procedures. Specify evidence, consent, scope, conflicts, TTL, review, deletion, procedural eval, canary, rollback, and audit. Name categories you would refuse to persist.
Solution guidance
Section titled “Solution guidance”Use explicit purpose/subject allowlists. Consent lookup binds authenticated principal and exact UI
and policy release. Structured claims can use (subject, predicate, object, qualifiers) while
retaining source prose as an artifact.
Atomic supersession uses compare-and-swap on the old generation. The outbox worker is idempotent, generation-fenced, and observable. Read selection excludes a record until required indexes publish its generation.
Check your understanding
Section titled “Check your understanding”- Which write inputs are independently obtained?
- Can a grant override policy?
- Does one source always suffice?
- Can contradiction be resolved by newest-wins?
- What must procedural evaluation bind?
- What happens when TTL exceeds policy?
- Which objects commit atomically?
- How do you prove deletion reached derived indexes?
- Why can a benign summary launder hostile origin?
Completion checklist
Section titled “Completion checklist”- Proposing, verifying, authorizing, and persisting are separate.
- Strict schemas reject unknown fields and duplicate identities.
- Policy and grant are intersected.
- Tenant, policy, purpose, kind, subject, principal, and time are bound.
- Evidence judgments cover exact sources.
- Unsupported evidence fails closed.
- Consent is purpose-, principal-, policy-, and retention-bound.
- Duplicate and contradiction scans precede commit.
- Supersession is explicit and atomic.
- Procedural promotion requires clean held-out evaluation.
- TTL is checked and bounded.
- Rejection writes nothing; acceptance revalidates one exact record.
- Receipt reproduction detects mutation.
- Publication and deletion propagation are durable and measured.
- Poisoning, laundering, stale-policy, and cross-scope mutations exist.
- Users can inspect, correct, revoke, export, and delete applicable memory.
Authoritative references
Section titled “Authoritative references”- OWASP, AI Agent Security Cheat Sheet
- OWASP GenAI Security Project, “Memory Is a Feature. It Is Also an Attack Surface”
- Dash et al., “From Untrusted Input to Trusted Memory”
- NIST, Privacy Framework FAQ
- Working, Episodic, Semantic, and Procedural Memory
mosaic-harness::memory_writeandlabs/harness/mp-22-memory-write-gate