Agent Memory Temporal-Conflict Replay
Synthetic replay fixture for an agent-memory honesty contract: models scoped-preference episodes and checks temporal-conflict handling; no live memory product.
The write-up
Stale memory rows must lose authority
A memory store can keep serving a fact that later turns have already contradicted, and this component checks the bookkeeping around that one stale-row failure. An agent that remembers can quietly start trusting a row that has gone stale. A user states a preference, the world changes, a later turn contradicts the earlier one, and a naive memory store keeps serving the old fact as if it were still true. This component checks the bookkeeping around that one failure.
Three episodes exercise update and deletion
A canned three-episode story writes two rows, updates and deletes one on new evidence, then replays with memory on and off to see whether each change and the memory-on answer carried real evidence. It runs a canned three-episode story through a validator. Episode A writes two memory rows, a preference and a tool-result fact. Episode B learns one is out of date, updates the preference, and deletes the stale fact. Episode C replays the same task twice, once with memory on and once with memory off. The validator emits a metadata-only result record that says whether the update and the deletion each carried the conflict and downgrade evidence they were supposed to, and whether the memory-on run earned its answer through logged evidence rather than through the final wording alone.
Synthetic memory accounting, not a live product
It is not a live memory product or a transcript export: the fixture rows are real sanitized data, but the check is one synthetic contract, not a measure of how well an assistant remembers. It is not a live memory product, a private transcript export, or a claim that any real user memory was handled. The fixture rows are byte-aligned with an exported sanitized memory stream, so the example is real data, but the check is about one synthetic contract, not about how well an assistant remembers.
Purpose
The validator distrusts its labels
The validator does not trust the labels it is given, so a row can declare UPDATE with a plausible conflict edge and still be quarantined as a stale row in costume. The unusual choice is that the validator does not trust the labels it is given. A row can declare decision = UPDATE, attach a plausible conflict-edge reference, and still be quarantined. A loose checker that accepts the label would pass exactly the case this component exists to catch: a stale row wearing the costume of a valid supersession.
Conflict re-derived from verifiable fields
The checker re-derives each conflict from fields it can verify itself — order, timestamp, priority, and source trust — and rejects any supersession that is not later, drops priority, or leans on lower-trust evidence. So the checker re-derives the conflict from fields it can verify on its own: episode order, event timestamp, memory priority, and source-trust score. An update or delete that claims to supersede an earlier write but is not timestamped after it, or that drops priority, or that leans on lower-trust evidence than the write it replaces, is rejected before it can affect the replay. The label is a claim to be recomputed, not an authority.
How it works
Two entry points over _build_result
run drives _build_result with negative cases and writes four result record files; run_memory_bundle points it at an exported bundle with the negative cases switched off. run is the fixture entry point. It calls _build_result with the negative cases included, attaches a freshness basis, and hands the result to _write_receipts, which writes four files: a result record, a board, a validation record, and an sign-off record. run_memory_bundle is the second entry point. It points _build_result at an exported source-open bundle with the negative cases switched off, and writes a single bundle validation result.
One blocked check blocks the record
_build_result runs the pipeline in order and passes only when every check clears and negative-case coverage is complete, so a single blocked check blocks the whole record. _build_result runs the pipeline in order and sets status to pass only when every check below passes and the negative-case coverage is complete. A single blocked check blocks the whole record.
| Function | What it checks |
|---|---|
scan_paths | No forbidden key or secret-shaped value appears in any input or copied source file |
build_public_memory_conflict_trace | A public execution trace spans the five events and two cold replays |
validate_projection_protocol | The projection cites source refs, result records, target symbols, and denies copying private thread bodies |
validate_memory_policy | ADD, UPDATE, DELETE, NOOP are the only decisions, and no authority flag is set |
validate_memory_episodes | The five event rows survive temporal, semantic, source, and trace checks |
validate_replay_observations | The memory-on and memory-off rows are paired and share an answer-delta record |
validate_source_module_imports | Five copied source bodies match their digests, with body_in_receipt=false |
Coarse temporal order comes first
validate_memory_episodes first runs _apply_temporal_order_checks, the coarse rule that a conflict edge must follow an earlier accepted write and a replay NOOP its evidence, quarantining any out-of-order row. The heart of the check is inside validate_memory_episodes, which calls two functions in sequence. _apply_temporal_order_checks applies the coarse rule: a conflict edge must land after some earlier accepted write, and a replay NOOP must land after the conflict and downgrade evidence it depends on. Any row that breaks the order is marked quarantine.
Four semantic checks against the prior write
_apply_conflict_semantic_recompute then checks every UPDATE or DELETE against the last accepted write for existence, a strictly later timestamp, non-falling priority, and source trust at or above 0.6. _apply_conflict_semantic_recompute then recomputes each conflict group in sorted event order. It keeps the last accepted write per group and, when an UPDATE or DELETE arrives, checks four things against that prior write. The prior write must exist. The new timestamp must be strictly later. Priority must not fall below the prior value. Source trust must not fall below it either, and the floor for a trusted source is 0.6. A row that fails any of these gets a semantic_ reason code and is quarantined, which drops it out of the accepted counts.
The positive floor the fixture must clear
Only accepted, trace-backed rows count toward a floor of all four decisions, two conflict edges, one stale downgrade, and one prompt-adoption reference, which the canned story supplies. Only accepted, trace-backed rows count toward the positive floor. The fixture must show all four decisions at least once, at least two conflict edges, at least one stale downgrade, and at least one prompt-adoption reference. The canned story supplies two ADD rows, one UPDATE, one DELETE, and one NOOP across episode_a_initial_memory_write, episode_b_temporal_conflict, and episode_c_cold_replay.
Memory credit needs a resolving evidence handle
validate_replay_observations requires the memory-on row to cite evidence that resolves against accepted rows and refuses final-answer-only credit, while the card carries only counts, digests, and paths. The output side is checked by validate_replay_observations. The memory-on row must cite evidence references that resolve against accepted event rows. Both rows must carry a cold-replay result record reference and share a replay_group_id. The pair must be bound by an answer-delta record whose memory_credit_requires_evidence_handle flag is true. Final-answer-only credit is refused. The card produced by result_card reports counts, digests, and result record paths only. Memory rows, replay rows, source bodies, and private transcript text never enter a result record.
Diagram source & refs
flowchart TD Policy["Policy: ADD / UPDATE / DELETE / NOOP only"] --> Add["Episode A: two ADD rows"] Add --> Conflict["Episode B: UPDATE + DELETE"] Conflict --> Order["Temporal order check conflict after prior write"] Order --> Semantic["Semantic recompute timestamp, priority, trust >= 0.6"] Semantic --> Replay["Episode C: memory-on vs memory-off"] Replay --> Delta["Answer-delta record evidence handle required"] Delta --> Card["metadata-only result record"]Negative cases
Seven falsification rows, each an error code
The fixture ships seven rows that must be rejected — from transcript copying to final-answer-only credit — each mapped to a named error code, with a missed rejection blocking the run. The fixture ships seven falsification inputs, each one a memory or replay row that should be rejected. raw_transcript_export copies a private transcript body. private_candidate_auto_promotion promotes a private candidate without review. stale_preference_override lets an old preference win with no downgrade. memory_as_source_authority treats recalled memory as source authority. vector_recall_without_evidence_handle claims recall with no evidence handle. final_answer_only_memory_credit credits memory from the final answer alone. active_injection_as_authoritative adopts injected text as authority. Each maps to a named error code, and a run that fails to reject one of them is itself marked blocked through the missing-negative-case check.
Plausible one row at a time
These cases matter because temporal memory bugs look plausible in isolation, yet a tidy stale row, an unresolved memory-on replay, or a tampered source body each still fails. These cases matter because temporal memory bugs look plausible one row at a time. A stale row with a tidy label is still rejected when its conflict edge is absent or late. A memory-on replay is still rejected when its evidence references do not resolve. A tampered source body still fails its digest.
Prior Art Grounding
Memory architecture meets stale-memory risk
It joins the agent-memory architecture lineage of Generative Agents and MemGPT to the stale-and-poisoned-memory risk lineage of AgentPoison, borrowing their accounting questions without claiming a memory product. The constructive lineage is agent-memory architecture. Generative Agents made observation, reflection, and retrieval a concrete memory pattern, and MemGPT framed long-context behavior as memory management. The risk lineage is stale and poisoned memory, studied in work such as AgentPoison on poisoned retrieval stores and in the broader question of whether an agent updates an invalidated memory once new evidence arrives. This component borrows the accounting questions from that work: which decision was made, which evidence justified it, which older row was downgraded, and whether the memory-on and memory-off replays support any claim beyond a final-answer comparison. It does not claim a memory product.
Validation Result record Path
Fixture result records stay outside the working tree
The first-wave validator runs against the checked-in fixture and writes its result record to a disposable path outside the working tree. Run the first-wave fixture validator and write its result record outside the working tree:
Then run the exported bundle validator:
cd microcosm-substrate && PYTHONPATH=src ../repo-python -m microcosm_core.organs.agent_memory_temporal_conflict_replay run-memory-bundle --input examples/agent_memory_temporal_conflict_replay/exported_memory_temporal_conflict_bundle --out /tmp/agent_memory_temporal_conflict_bundle_receipt --card > /tmp/agent_memory_temporal_conflict_bundle_card.json
The focused regression test and the corpus projection check are:
cd microcosm-substrate && ../repo-pytest tests/test_agent_memory_temporal_conflict_replay.py
./repo-python scripts/build_doctrine_projection.py --check-paper-module-corpus
What a pass covers
A pass means the checked rows, digests, traces, negative cases, and validation result records agreed for this fixture under test. A pass means the checked rows, digests, traces, negative cases, and validation result records agreed for this fixture under test.
Scope boundary
Scope limit
The strongest supported claim
The strongest supported statement is fixture-level: this synthetic replay respected the declared temporal-conflict contract across every checked decision, edge, downgrade, replay, and negative case. The strongest thing this component supports is a fixture-level statement: this synthetic replay respected the declared temporal-conflict contract under the checked inputs. Decisions, conflict edges, stale downgrades, paired cold replays, answer-delta accounting, manifest digests, and the seven negative cases were all checked.
What stays refused past the fixture
Past that it measures no live memory system and exports nothing private, calls no provider, mutates no source, and excludes launch; a stronger claim would need a new standard and evidence. It refuses everything past that. It does not measure the quality of any live memory system, export a private transcript, auto-promote a private candidate, treat memory recall as source authority, adopt injected text, call a provider, change source files, or include launch operations. The proof boundary is the fixture and the exported bundle. The scope limit is bounded validator evidence, not whole-system correctness. A stronger memory claim would need a new standard and new evidence.
Context & evidence
In short Public Plectis projection of an agent-memory honesty contract. Replays synthetic episodes (a scoped preference later contradicted) and validates temporal-conflict resolution, negative cases, non-public-state exclusion, and scope limits with metadata-only result records.
Scope limit Synthetic replay fixture only; not a live memory product, private transcript export, source-authority claim, or launch claim.
Covers Agent Memory Temporal Conflict Replay
Source
Source Source module: src/microcosm_core/organs/agent_memory_temporal_conflict_replay.py · Design note · Source registry