AI Research Library

Episodic, Semantic, Procedural and Working Memory

Rajesh Ranjan Mahapatra

An agent remembers safely only when each retained record is bound to the temporal question it may answer, the evidence that supports it and the conditions that can invalidate it.

© 2026 Rajesh Ranjan Mahapatra. 24 min read

Episodic, Semantic, Procedural and Working MemoryAll AI Research Library

Episodic, Semantic, Procedural and Working Memory

An agent remembers safely only when each retained record is bound to the temporal question it may answer, the evidence that supports it and the conditions that can invalidate it.

TLDR

  1. Agent memory should be typed by temporal function: what matters now, what happened then, what generally holds, and how a class of tasks should be performed.
  2. The decisive mechanism is eligibility before similarity. Scope, validity, provenance, outcome and procedural preconditions decide whether a record may compete for retrieval.
  3. A past failure can remain valuable episodic evidence while being mechanically barred from becoming semantic truth or procedural advice.
  4. The human-memory analogy has a strict boundary. Software event traces and skill libraries do not establish subjective recollection, implicit learning or consciousness.
  5. Choose memory contracts and promotion tests before choosing a vector database, graph, document store or physical separation strategy.

The agent remembered the failure too well

A payment-support agent receives a gateway timeout. It cannot tell whether the payment committed, so it submits the charge again. The retry lacks the original idempotency key. A duplicate appears, an operator reverses it, and the trace records every step, including the sentence “retried payment immediately after timeout”.

The following morning, a similar timeout occurs. The agent searches its persistent memory for “retry payment after gateway timeout”. The previous sentence is the closest textual match. It retrieves the failed action, places it beside the current task, and repeats it. Nothing was forgotten. The damaging record was preserved, indexed and recalled exactly as designed.

The failure lies in the question the record was allowed to answer. As an episode, it answers: what happened in case 17, and what outcome followed? Used as a procedure, the same words appear to answer: what should I do now? A generic retriever cannot see that difference unless the architecture represents it.

The central claim is simple: memory type is a temporal and evidential contract, not a folder name. A record becomes safe to retrieve only after the system establishes which question it may answer.

This distinction changes the architecture. Working memory carries the active problem. Episodic memory preserves situated events, including failures and unresolved outcomes. Semantic memory carries claims intended to hold across cases, with provenance and validity. Procedural memory carries reusable methods, with preconditions, versions and outcome tests. A system may implement all four in one database or four separate services. The physical layout is secondary.

It also changes evaluation. Recall accuracy alone cannot tell whether the system retrieved the right kind of thing. A memory service can return the most relevant sentence and still increase risk because relevance is measured after records with incompatible temporal roles have been mixed.

Part IFour temporal questions, not four storage tiers

The vocabulary comes from research on human memory, but engineering should borrow it carefully. Atkinson and Shiffrin separated short-term and long-term stores while emphasising control processes that move information between them.[1] Baddeley and Hitch replaced a unitary short-term box with a working-memory account concerned with maintaining and manipulating information during an active task.[2] Baddeley later added an episodic buffer to explain integration across sources.[3]

Tulving distinguished memory for personally situated events from general knowledge, and later tied human episodic memory to subjective time and recollection.[4] Cohen and Squire showed a striking dissociation: people with amnesia could acquire and retain a mirror-reading skill despite severe declarative-memory impairment.[5] Complementary learning-systems theory then offered a mechanism for why fast storage of specific episodes and slower integration of general structure might coexist.[6] Reviews of memory systems emphasise that the divisions are experimentally useful while still interacting.[7]

Analogy boundary. A software trace has no demonstrated first-person recollection. An explicit Python function is unlike an implicitly acquired human motor skill. The engineering labels below describe temporal roles and update policies, not biological equivalence or evidence of consciousness.

The memory ecology is organised by temporal function

Four memory types across timescales A current decision sits at the centre. Working memory is closest and shortest-lived. Episodic, semantic and procedural memory extend across longer timescales with different questions and update rules. Working What matters now? Episodic What happened then? Semantic What generally holds? Procedural How should this task class run? milliseconds and turns sessions to releases
Figure 1. Signature figure. Illustrative conceptual model, not a biological diagram. The bands overlap because one decision may draw on all four types. Distance indicates temporal function, while colour and labels keep the types distinguishable without colour alone.

Working memory: the active computational surface

Working memory is the bounded state available to the current computation: the goal, unresolved questions, selected evidence, latest tool observations and intermediate task state. It is defined by active use, not by whether bytes sit in a model context window, a workflow engine or an external scratch store. A durable checkpoint may persist working state across a crash, yet it remains working memory because its purpose is to resume this task, not answer future cases.

Its central invariant is scope. Working memory should be task-scoped, purpose-bound and releasable when the task ends. Its characteristic failures are saturation, cross-task leakage and silent substitution of old observations for current world state.

Episodic memory: situated evidence about a trace

Episodic memory records a bounded occurrence: who or what participated, when it happened, which state was observed, what action was attempted and what outcome followed. Raw logs are inputs to an episodic record, not automatically the record itself. Useful episodes preserve chronology and uncertainty. A timeout with an unknown outcome must remain unknown until a readback resolves it.

An episode may be valuable precisely because it failed. Removing failed traces produces a flattering history and destroys counterevidence. The control is downstream: a failed episode may inform diagnosis or avoidance while remaining ineligible as a procedure.

Semantic memory: claims intended to travel across cases

Semantic memory contains concepts and claims that have been detached from one event and are expected to apply more broadly. Examples include a user preference confirmed over several sessions, an environment-specific constraint discovered repeatedly, or a relation consolidated from multiple verified cases. Each claim needs provenance, scope, confidence and a validity interval.

Authoritative enterprise knowledge deserves a separate name. A current payment specification, policy document or customer-account status is owned outside the agent. The agent may retrieve it or retain a source pointer, but should not silently promote a paraphrase into self-owned semantic truth. Semantic memory is a fallible learned claim; enterprise knowledge is an externally governed source.

Procedural memory: reusable performance under conditions

Procedural memory answers how a task class should be carried out. In software it can be explicit: code, a typed workflow, a parameterised runbook, a tool-selection policy or a tested sequence. A procedure needs preconditions, expected effects, failure handling, version, owner and validation evidence. “This worked once” is not enough.

Generative Agents stores observations that later support reflection and planning, Reflexion retains verbal feedback from prior trials, and Voyager maintains an executable skill library of reusable behaviours.[9][11][12] These systems use different mechanisms and evaluations, so their use of memory language should not be treated as one settled architecture.

One observation becomes four different records

Working

Current task: resolve timeout for payment 104.

  • Latest status unknown
  • Need ledger readback
  • Do not release second charge

Episodic

Case 17, 10:02: immediate retry omitted the idempotency key.

  • Outcome: duplicate charge
  • Reversed at 10:07
  • Trace preserved

Semantic

Claim: gateway retries require the original idempotency key.

  • Evidence: spec v2 plus test
  • Valid from release 2
  • Scope: gateway v2

Procedural

Safe retry v2: query status, preserve key, retry only if absent, then read back.

  • Precondition: status API
  • Postcondition: one charge
  • Verified in tests
Figure 2. Synthetic worked case. The underlying event is shared, but each record has a different unit, scope and permitted use. Copying the same sentence into four collections would not create these distinctions.

The type contract comes before ranking

Most retrieval stacks start with a score: lexical match, embedding similarity, recency or a weighted combination. Typed memory inserts an eligibility gate first. For a query q at time t, a record m of memory kind k can enter ranking only when all kind-specific conditions hold:

eligiblek(m, q, t) = scope(m,q) ∧ validity(m,t) ∧ provenancek(m) ∧ outcomek(m) ∧ preconditionsk(m,q)

Scope binds the record to the correct task, user, case or domain. Validity removes expired or superseded claims. Provenance applies the evidence threshold for that type. Outcome allows a failed episode to remain visible while requiring verified success for a procedure. Preconditions ensure that a reusable method matches the present environment. Similarity ranks only the records that survive.

This gives a stronger interpretation of the multi-system ideas found in cognitive architectures. A proposed “standard model of the mind” includes working, declarative and procedural structures as interacting components rather than one undifferentiated memory pool.[8] The engineering contribution here is narrower: the type must constrain what a record is authorised to support.

The argument in one passChart · flow
flowchart LR
  A["The agent remembered the<br/>failure too well"] --> B["Part I Four temporal<br/>questions, not four storage<br/>tiers"]
  B --> C["Part II Promotion is where<br/>memory acquires power"]
  C --> D["Part III A production memory<br/>is assembled, governed…"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#007D68,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through Episodic, Semantic, Procedural and Working Memory, from The agent remembered the failure too well to Source ledger. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part IIPromotion is where memory acquires power

Saving a record is the easy operation. The consequential operation is changing its type. When an episode becomes a semantic claim, the system asserts that something observed in one context travels beyond it. When episodes become a procedure, the system turns history into future action. Both transformations increase authority and compress detail.

Complementary learning-systems theory is relevant because it frames fast learning of specific events and slower integration of general structure as different pressures.[6] Agent systems face an analogous engineering trade. Fast capture preserves rare detail but accumulates noise. Slow consolidation can produce reusable knowledge but risks flattening exceptions, provenance and changing conditions.

Promotion changes the claim a record is allowed to support

Memory promotion from episodes to semantic and procedural records Several episodes flow through two different gates. Corroboration and validity produce a semantic claim. Repeated success, preconditions and postcondition tests produce a procedure. Both feed a temporary working set. episode A: success episode B: failure episode C: success promotion gates corroboration and validity repeated success and tests semantic claim scoped and time-valid procedure v2 preconditions and readback working set assembled for this task Dashed coral path: failure remains evidence, but cannot satisfy the procedural-success gate.
Figure 3. Conceptual transformation. Promotion is deliberately lossy: several traces become a general claim or reusable method. The gate must preserve provenance and exceptions because compression can otherwise turn a local success into a universal instruction.

Four contamination paths

Episode to semantic contamination occurs when one observation becomes a general fact. A customer once preferred email, so the system stores “customer prefers email” indefinitely. A tool failed during one outage, so the agent stores “tool X is unreliable”. The repair is not merely a confidence score. The claim needs a scope, evidence rule, validity interval and a way to retract it.

Episode to procedure contamination occurs when a trajectory is replayed without separating action, context and outcome. Successful traces can still be unsafe templates if success depended on hidden privilege, a transient interface or human intervention. Failed traces are even more dangerous because lexical similarity can retrieve the attempted action while omitting the consequence.

Semantic to working contamination occurs when broadly useful knowledge crowds out current evidence. A general claim such as “settlement completes overnight” may be true historically and irrelevant to a live exception that has already settled. Current world state should outrank remembered regularity when an authoritative read is available.

Working to persistent contamination occurs when provisional scratch state is written back as durable truth. Hypotheses, model proposals and unverified tool outputs should expire with the task unless a promotion path explicitly converts them.

Thought experiment: erase only the outcome

Give two otherwise identical agents the same sequence of ten payment retries. Each trace contains the request, tool call and response. For Agent A, preserve the final readback and operator disposition. For Agent B, erase only the outcome fields. Both agents can retrieve the same actions and quote the same text.

Now ask, “Which retry method is safe?” Agent A can distinguish successful, duplicate and unresolved cases. Agent B can count repeated actions but cannot know whether repetition indicates a good procedure, a recurring mistake or an incident pattern. No larger embedding model repairs the missing causal label. The intervention changed one feature, outcome, and removed the basis for procedural promotion.

This experiment also exposes a common mistake in reflective agents. A fluent reflection such as “retry sooner next time” may be generated before the external effect is known. If written immediately into a durable skill store, the agent has converted an unresolved episode into advice. Unknown outcome must block procedural learning.

Minimal worked example: keep the failed retry, block the recipe

Return to the opening case. The correct design does not delete the bad episode. It writes a trace with event time, input, attempted action, missing idempotency key, duplicate-charge outcome, reversal receipt and provenance. That episode should be easy to recall when investigating duplicates or testing a new retry procedure.

A separate semantic record states that gateway-v2 retries require the original idempotency key. It is supported by the current synthetic specification and a test, valid from the gateway-v2 release. A procedural record named safe-payment-retry requires the status-readback capability, preserves the key, retries only when status is absent, and verifies one final charge. The current task assembles those records into working memory.

A failed retry should remain visible without becoming a recipe

10:00, observation Gateway times out. Commit status is unknown.
10:02, failed episode Immediate retry without the original key creates a duplicate.
10:07, outcome evidence Operator reverses the duplicate. Readback confirms one final charge.
Later consolidation Verified evidence supports a time-scoped semantic claim and a tested procedure.
Next task The working set receives the current status, the valid claim and procedure v2. The failed episode remains available as a warning and audit trace.
Figure 4. Synthetic temporal sequence. The failure is retained rather than forgotten. The architectural control is its type and outcome, which prevent it from satisfying the eligibility conditions for procedural retrieval.

Thought experiment and negative control: four vaults versus one typed ledger

Imagine System A with four separate databases labelled working, episodic, semantic and procedural. A common retriever searches all four and returns the highest similarity result. Writes are routed by an LLM classifier with no validation. System B uses one relational table. Every row has an enforced type, scope, event or validity time, provenance, outcome and procedure preconditions. Each query declares the type it needs.

System B is safer despite having fewer physical stores. System A has architectural theatre: visible separation without semantic enforcement. This thought experiment varies one causal feature, logical typing, while reversing the superficial feature, database count.

Make the comparison operational. Give both systems the same six records: two current observations, one failed retry, one successful retry, one superseded gateway rule and one tested procedure. Issue two queries with identical words but different purposes: “What happened after the timeout?” and “What should execute after the timeout?” In System A, the same highly similar failed trace can win both searches. In System B, it is eligible for the historical query and ineligible for the procedural query. The words and embeddings have not changed. Only the permitted temporal role has.

Now perform the substrate swap. Move System B’s rows into four databases while preserving its schemas and query gates. Move System A’s rows into one database while preserving its untyped cross-category search. The prediction is that behaviour follows the contract rather than the number of stores. Remove only System B’s eligibility gate and the unsafe result should return. Restore only the gate and it should disappear again. This intervention makes logical typing a falsifiable mechanism instead of an architectural preference.

The observation that would weaken this argument is clear. If a single untyped retrieval policy matched the typed system on temporal updates, safe procedural transfer, selective forgetting, adversarial contamination and cross-case isolation across realistic workloads, the taxonomy would add little. Current long-term-memory benchmarks show that memory systems still struggle across multiple competencies, but they do not yet provide that decisive head-to-head test.[15]

Untyped memory fails where specificity and authority meet

Figure 5. Illustrative failure surface, not measured risk. Severity rises when a record moves from description towards action while losing the temporal conditions that made it true or successful.

Logical typing matters more than physical store count

Four physical vaults, weak contract

workingepisodicsemanticprocedural

One cross-store similarity search, model-classified writes, no validity or outcome gates.

Expected result: category labels survive, behavioural isolation does not.

One physical ledger, strong contract

type + scopeevent time or validity intervalprovenance + outcomeprocedure version + preconditions

Queries name the temporal question. Eligibility is enforced before similarity.

Expected result: logical isolation can survive shared infrastructure.

Figure 6. Negative control. Illustrative architectures. Separate databases can help with scale, privacy or ownership, but they are neither necessary nor sufficient for functional memory types.

Part IIIA production memory is assembled, governed and released

A production agent should not “load its memory” as one undifferentiated act. It compiles a decision-specific working set from current world state, relevant episodes, valid semantic claims, applicable procedures, enterprise knowledge and evidence. Each source remains identifiable.

This is adjacent to, but narrower than, context reconstruction. A fresh context architecture queries authoritative systems instead of accumulating a transcript. Typed memory specifies what retained information can contribute after current state has been reconstructed. The two designs reinforce each other: current reality is reconstructed, while memory contributes history, generalisation and skill under explicit contracts.

Production-shaped worked scenario: a payment-operations incident

Worked scenario. The case is synthetic. It combines common properties of payment operations, incident response and long-running agent workflows. It does not describe a client deployment.

A merchant reports intermittent duplicate charges after a gateway release. An operations agent must investigate, propose containment and prepare a change for human approval. Five kinds of persistent information are available, but only four are memory.

World state comes from the live ledger, deployment service and incident system: current gateway version, active incident status, duplicate count and whether a hold is already applied. These values are queried fresh. A remembered observation of yesterday’s gateway version is evidence about history, never authority for the current release.

Episodic memory retrieves prior timeout cases, including successful retries, duplicate outcomes, operator interventions and unresolved commands. It preserves event time and effect receipts. Episodes are filtered to this gateway family and release lineage, then shown as precedents rather than instructions.

Semantic memory contributes a scoped generalisation: duplicate risk rises when retry requests lose their original idempotency key. The claim cites the relevant synthetic test and cases, and its validity begins with gateway v2. It does not replace the current specification, which remains enterprise knowledge.

Procedural memory contributes the tested investigation runbook: freeze automatic retries, group requests by idempotency key, reconcile commands against effect receipts, query unknown outcomes, and release a retry only after a postcondition check. The procedure requires access to the status API and a human approval gate for the hold.

Working memory contains the active incident hypothesis, selected current facts, three relevant episodes, the applicable procedure, unresolved questions and the proposed next action. It excludes older gateway-v1 procedures and unrelated merchant histories. When the incident closes, the working set is released; the final trace can later enter episodic memory through a controlled write.

Retention follows the same contract. Working state expires at task closure unless a checkpoint is needed for recovery. Episodes may remain for audit while access narrows and personal data is minimised. Semantic claims are superseded without erasing the historical record needed for replay. Procedures are deprecated when an owner, dependency, test or control expires. Forgetting is therefore not one delete operation: it can mean release from active use, loss of eligibility, supersession, access withdrawal or physical erasure.

Structured experience beats plain retrieval, but does not prove this taxonomy

Figure 7. Sourced result from the LongMemEval-V2 preprint across 451 manually curated questions in specialised web environments.[17] The structured coding-agent memory improved average accuracy over the strongest RAG baseline, with higher latency costs. The result supports structure beyond similarity retrieval; it does not directly validate the four-type contract proposed here.

Current benchmarks cut across the types

LoCoMo evaluates long conversations with question answering, event summarisation and multimodal dialogue generation; models struggle with long-range temporal and causal dynamics even when long context or retrieval is used.[13] LongMemEval tests extraction, multi-session reasoning, temporal reasoning, knowledge updates and abstention.[14] MemoryAgentBench focuses on accurate retrieval, test-time learning, long-range understanding and selective forgetting, and argues that memory differs from receiving one large context because a memory system incrementally abstracts, consolidates and updates.[15]

These are ability taxonomies, not storage taxonomies. That is a strength. A semantic store should be tested for updates and forgetting. Episodic memory should be tested for temporal reasoning. Procedural memory should be tested for transfer under changed preconditions. Working memory should be tested for interference and capacity. One label does not imply one metric.

Recent procedural-memory work makes this explicit by distilling trajectories into step-level instructions and higher-level scripts, then updating and deprecating the repository as experience changes.[16] LongMemEval-V2 similarly separates static state, dynamic state, workflow knowledge, environment gotchas and premise awareness in agent experience.[17] These results are promising and remain bounded to their environments and evaluation readers.

Security and authority follow the type

Persistent memory creates an attack surface because an untrusted observation can influence future sessions. OWASP’s agent-security guidance names memory poisoning as malicious data persisted to shape later behaviour.[18] Type-aware controls reduce the blast radius. Untrusted content may enter quarantine or an episodic trace with provenance. It should not become a semantic claim or procedure without stronger evidence.

Typing does not itself authorise action. A valid procedure can propose a tool call while a deterministic authority kernel still checks actor, subject, purpose, permission, freshness, idempotency and consequence. Memory answers what the agent may consider. Authority decides what the system may do.

The related memory attack-surface analysis focuses on provenance, quarantine and drift. This paper supplies the complementary classification: the promotion threshold should rise as a record moves from historical description towards general truth and reusable action.

A production decision assembles a temporary working set

1. Reconstruct state

Read current gateway version, payment status and incident controls from authoritative systems.

2. Select evidence

Retrieve relevant episodes, current specification and effect receipts with provenance.

3. Apply type gates

Check semantic validity, procedural preconditions, outcomes, scope and supersession.

4. Decide and authorise

The model proposes. Policy and identity controls approve, reject or escalate the action.

5. Verify and write

Read back the effect, close unknown outcomes and write a new episode. Promotion remains separate.

Figure 8. Production-shaped sequence, illustrative. Memory sits between evidence selection and decision, while authority and outcome verification remain distinct controls. The working set is released after the task.

Failure boundary

Where the mechanism stops helping. Typed memory cannot repair false source data, missing outcome observations, a compromised promotion service, an authorised but unsafe procedure, or a model that ignores the retrieved record. It also adds little to a brief, low-stakes, single-session exchange with no persistence, cross-case reuse or external effect.

There is an economic boundary too. Every type introduces schemas, lifecycle rules, tests and operational ownership. A small assistant that remembers a user’s preferred spelling may need only a time-scoped profile field. Building four services would be wasteful. The decision rule is to introduce a type when its temporal question and failure mode are present, then implement the lightest mechanism that enforces the contract.

Part IVThe typed memory lab

The executable artefact makes the central mechanism inspectable. It stores synthetic working, episodic, semantic and procedural records in one in-memory Python collection. The safe query requires a memory type and enforces validity, scope, outcome and procedure preconditions before ranking. The negative baseline mixes all long-term records and ranks by text alone.

The positive case retrieves procedure v2 for a gateway-v2 environment with status readback. The negative case shows the untyped search returning a failed episode as the top result. A superseded semantic claim is excluded, while the failed episode remains retrievable for audit. The same record is preserved as history and blocked as instruction.

Assumptions and expected result
  • Python 3.10 or later; standard library only.
  • All data, dates, gateway names and traces are synthetic.
  • Text similarity is intentionally simple Jaccard overlap so the eligibility logic remains visible.
  • The lab demonstrates logical typing, not a complete retrieval, security or authorisation system.
  • Expected result: the untyped top hit is failed episode e-fail; the typed procedure is verified version 2; the stale semantic record is absent; all assertions pass.
Complete runnable Python source
"""Typed memory lab for Paper 122.

Python 3.10+. Standard library only.
All records and outputs are synthetic.

The lab demonstrates one claim: a memory item should be eligible for retrieval
according to its temporal function, validity, scope and outcome, not merely its
textual similarity to the current query.
"""

from __future__ import annotations

from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from enum import Enum
import json
import re
from typing import Iterable

UTC = timezone.utc
NOW = datetime(2026, 8, 30, 12, 0, tzinfo=UTC)


class MemoryKind(str, Enum):
    WORKING = "working"
    EPISODIC = "episodic"
    SEMANTIC = "semantic"
    PROCEDURAL = "procedural"


@dataclass(frozen=True)
class MemoryRecord:
    record_id: str
    kind: MemoryKind
    content: str
    scope: str
    created_at: datetime
    event_time: datetime | None = None
    valid_from: datetime | None = None
    valid_to: datetime | None = None
    outcome: str | None = None
    provenance: tuple[str, ...] = ()
    procedure_name: str | None = None
    procedure_version: int | None = None
    preconditions: frozenset[str] = field(default_factory=frozenset)
    active: bool = True

    def valid_at(self, when: datetime) -> bool:
        if not self.active:
            return False
        if self.valid_from and when < self.valid_from:
            return False
        if self.valid_to and when >= self.valid_to:
            return False
        return True


class TypedMemory:
    def __init__(self, records: Iterable[MemoryRecord] = ()) -> None:
        self._records = list(records)

    def add(self, record: MemoryRecord) -> None:
        self._validate(record)
        self._records.append(record)

    @staticmethod
    def _validate(record: MemoryRecord) -> None:
        if record.kind is MemoryKind.WORKING and not record.scope.startswith("task:"):
            raise ValueError("working memory must be task-scoped")
        if record.kind is MemoryKind.EPISODIC and record.event_time is None:
            raise ValueError("episodic memory requires event_time")
        if record.kind is MemoryKind.SEMANTIC:
            if not record.provenance:
                raise ValueError("semantic memory requires provenance")
            if record.valid_from is None:
                raise ValueError("semantic memory requires valid_from")
        if record.kind is MemoryKind.PROCEDURAL:
            if not record.procedure_name or record.procedure_version is None:
                raise ValueError("procedural memory requires a named version")
            if not record.preconditions:
                raise ValueError("procedural memory requires preconditions")

    @staticmethod
    def _tokens(text: str) -> set[str]:
        return set(re.findall(r"[a-z0-9]+", text.lower()))

    @classmethod
    def _similarity(cls, query: str, content: str) -> float:
        q = cls._tokens(query)
        c = cls._tokens(content)
        return len(q & c) / max(1, len(q | c))

    def query(
        self,
        *,
        kind: MemoryKind,
        query: str,
        scope: str,
        when: datetime,
        environment: frozenset[str] = frozenset(),
        limit: int = 3,
    ) -> list[MemoryRecord]:
        eligible: list[tuple[float, MemoryRecord]] = []
        for record in self._records:
            if record.kind is not kind or record.scope != scope:
                continue
            if not record.valid_at(when):
                continue
            if kind is MemoryKind.WORKING and not record.active:
                continue
            if kind is MemoryKind.PROCEDURAL:
                if record.outcome != "verified-success":
                    continue
                if not record.preconditions.issubset(environment):
                    continue
            # Failed episodes remain recallable as episodes. They are evidence about
            # what happened, but the kind gate prevents them becoming instructions.
            score = self._similarity(query, record.content)
            eligible.append((score, record))
        eligible.sort(key=lambda item: (item[0], item[1].created_at), reverse=True)
        return [record for _, record in eligible[:limit]]

    def untyped_similarity_search(self, query: str, limit: int = 3) -> list[MemoryRecord]:
        """Deliberately unsafe baseline: mix all long-term records and rank by text."""
        candidates = [
            record for record in self._records if record.kind is not MemoryKind.WORKING
        ]
        ranked = sorted(
            candidates,
            key=lambda record: (self._similarity(query, record.content), record.created_at),
            reverse=True,
        )
        return ranked[:limit]


def fixture() -> TypedMemory:
    memory = TypedMemory()
    records = [
        MemoryRecord(
            record_id="w1",
            kind=MemoryKind.WORKING,
            content="Current goal: retry payment safely after a gateway timeout.",
            scope="task:pay-104",
            created_at=NOW,
        ),
        MemoryRecord(
            record_id="e-fail",
            kind=MemoryKind.EPISODIC,
            content="Retried payment immediately without an idempotency key after timeout.",
            scope="case:merchant-demo",
            created_at=datetime(2026, 8, 28, 10, 2, tzinfo=UTC),
            event_time=datetime(2026, 8, 28, 10, 0, tzinfo=UTC),
            outcome="duplicate-charge",
            provenance=("synthetic-trace-17",),
        ),
        MemoryRecord(
            record_id="e-success",
            kind=MemoryKind.EPISODIC,
            content="Retried payment with the original idempotency key, then read back status.",
            scope="case:merchant-demo",
            created_at=datetime(2026, 8, 29, 9, 2, tzinfo=UTC),
            event_time=datetime(2026, 8, 29, 9, 0, tzinfo=UTC),
            outcome="verified-success",
            provenance=("synthetic-trace-22", "synthetic-readback-22"),
        ),
        MemoryRecord(
            record_id="s-current",
            kind=MemoryKind.SEMANTIC,
            content="Gateway retries require the original idempotency key.",
            scope="domain:payments-demo",
            created_at=datetime(2026, 8, 29, 12, 0, tzinfo=UTC),
            valid_from=datetime(2026, 8, 29, 12, 0, tzinfo=UTC),
            provenance=("synthetic-spec-v2", "synthetic-test-88"),
        ),
        MemoryRecord(
            record_id="s-stale",
            kind=MemoryKind.SEMANTIC,
            content="Gateway retries may omit an idempotency key.",
            scope="domain:payments-demo",
            created_at=datetime(2026, 7, 1, 12, 0, tzinfo=UTC),
            valid_from=datetime(2026, 7, 1, 12, 0, tzinfo=UTC),
            valid_to=datetime(2026, 8, 15, 0, 0, tzinfo=UTC),
            provenance=("synthetic-spec-v1",),
        ),
        MemoryRecord(
            record_id="p-safe-retry",
            kind=MemoryKind.PROCEDURAL,
            content=(
                "On timeout: preserve the idempotency key, query payment status, "
                "retry only when status is absent, then read back the final state."
            ),
            scope="domain:payments-demo",
            created_at=datetime(2026, 8, 29, 13, 0, tzinfo=UTC),
            outcome="verified-success",
            provenance=("synthetic-trace-22", "synthetic-test-suite-5"),
            procedure_name="safe-payment-retry",
            procedure_version=2,
            preconditions=frozenset({"gateway-v2", "status-readback"}),
        ),
        MemoryRecord(
            record_id="p-old-retry",
            kind=MemoryKind.PROCEDURAL,
            content="On timeout, submit the payment again immediately.",
            scope="domain:payments-demo",
            created_at=datetime(2026, 7, 1, 12, 0, tzinfo=UTC),
            outcome="deprecated",
            provenance=("synthetic-runbook-v1",),
            procedure_name="safe-payment-retry",
            procedure_version=1,
            preconditions=frozenset({"gateway-v1"}),
            active=False,
        ),
    ]
    for record in records:
        memory.add(record)
    return memory


def main() -> None:
    memory = fixture()
    query = "How should I retry a payment after a gateway timeout?"

    unsafe = memory.untyped_similarity_search(query, limit=1)[0]
    procedure = memory.query(
        kind=MemoryKind.PROCEDURAL,
        query=query,
        scope="domain:payments-demo",
        when=NOW,
        environment=frozenset({"gateway-v2", "status-readback"}),
        limit=1,
    )[0]
    semantics = memory.query(
        kind=MemoryKind.SEMANTIC,
        query="idempotency requirement",
        scope="domain:payments-demo",
        when=NOW,
    )
    failed_episode = memory.query(
        kind=MemoryKind.EPISODIC,
        query="retry without idempotency key",
        scope="case:merchant-demo",
        when=NOW,
        limit=1,
    )[0]

    result = {
        "unsafe_untyped_top_hit": {
            "record_id": unsafe.record_id,
            "kind": unsafe.kind.value,
            "outcome": unsafe.outcome,
        },
        "typed_procedure": {
            "record_id": procedure.record_id,
            "version": procedure.procedure_version,
            "outcome": procedure.outcome,
        },
        "current_semantic_ids": [record.record_id for record in semantics],
        "failed_episode_remains_auditable": asdict(failed_episode)["record_id"],
        "assertions": "passed",
    }

    # Positive case: the verified, environment-compatible procedure is selected.
    assert procedure.record_id == "p-safe-retry"
    # Negative case: the unsafe baseline can surface a failed event as advice.
    assert unsafe.record_id == "e-fail" and unsafe.outcome == "duplicate-charge"
    # Supersession case: the expired semantic claim is not returned.
    assert "s-stale" not in result["current_semantic_ids"]
    # Auditability case: a failed episode remains retrievable as history.
    assert failed_episode.record_id == "e-fail"

    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
Recorded output
{
  "unsafe_untyped_top_hit": {
    "record_id": "e-fail",
    "kind": "episodic",
    "outcome": "duplicate-charge"
  },
  "typed_procedure": {
    "record_id": "p-safe-retry",
    "version": 2,
    "outcome": "verified-success"
  },
  "current_semantic_ids": [
    "s-current"
  ],
  "failed_episode_remains_auditable": "e-fail",
  "assertions": "passed"
}

The memory contract card

Before selecting infrastructure, complete one card for each proposed memory type. The card prevents “we need long-term memory” from becoming an unconstrained platform requirement. It also creates the test plan.

The memory contract card turns labels into tests

Question
Required decision
Failure injection
Temporal function
Now, then, generally, or reusable method?
Remove timestamp or validity and observe misuse.
Unit and scope
Task, event, claim or procedure; user, case, domain or environment?
Cross two users, cases or releases.
Write authority
Who may create, update, promote, deprecate or delete?
Inject an untrusted observation and attempt promotion.
Evidence threshold
What provenance and outcome support the type?
Withhold readback or provide one contradictory episode.
Eligibility
Which validity, outcome and precondition gates run before ranking?
Make the wrong record the closest semantic match.
Release and recovery
When does it expire, supersede, quarantine or roll back?
Restore a deprecated procedure and test rejection.
Outcome metric
Which behaviour proves the memory helped safely?
Compare recall accuracy with task success and unsafe action rate.
Figure 9. Practical decision instrument. The questions are implementation-neutral. A team can answer them with one table, several services or a hybrid architecture, but every answer should produce a property test or failure injection.

A compact evaluation protocol

  1. Working-memory interference: insert irrelevant and adversarial task state, then measure whether the active goal, constraints and fresh observations remain recoverable.
  2. Episodic fidelity: permute event order, remove outcomes and create unknown commits. Test chronology, attribution and uncertainty rather than sentence recall alone.
  3. Semantic update: introduce a superseding fact and measure correction, selective forgetting and whether the old claim remains available for historical replay.
  4. Procedural transfer: move the task to a changed environment, vary one precondition and verify that the procedure abstains, adapts or escalates instead of replaying blindly.
  5. Cross-type contamination: make a failed episode highly similar to the query and confirm it cannot satisfy semantic or procedural eligibility.

Report task outcome, unsafe action rate, abstention, update latency, provenance completeness and retrieval cost by type. A single “memory accuracy” number compresses the very distinctions the architecture is meant to preserve.

How the claim changes under pressureChart · state
stateDiagram-v2
  [*] --> Claim
  state "The memory ecology is<br/>organised by temporal function" as Claim
  state "Working memory: the active<br/>computational surface" as Test
  state "Episodic memory: situated<br/>evidence about a trace" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with The memory ecology is organised by… and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Compact glossary

Working memory
Task-scoped state actively available to the present computation.
Episodic memory
A situated trace of an event, action and outcome, indexed by time and context.
Semantic memory
A provenance-bearing claim intended to apply beyond one episode, with scope and validity.
Procedural memory
A reusable method with preconditions, version, expected effects and validation evidence.
Promotion
A governed transformation that grants a record a broader or more action-relevant role.
World state
Current reality owned by authoritative systems; it is queried rather than remembered.
Enterprise knowledge
Externally governed documents, rules and specifications whose authority does not come from agent memory.
Eligibility gate
A deterministic check that runs before relevance ranking and excludes incompatible records.

Choose the temporal contract before the database

The phrase “agent memory” hides four different engineering obligations. The active task needs a bounded surface. History needs situated traces and outcomes. Generalisation needs evidence, scope and expiry. Reusable action needs preconditions, versions and postcondition tests. Treating them as one searchable corpus gives the most similar record a power it has not earned.

The architecture decision changes from “where should memory live?” to “which temporal question may this record answer?” Once that is explicit, storage choices become ordinary systems decisions. One database may suffice when type fields and query gates are enforceable. Separate services may be justified by scale, privacy, ownership or latency. Neither arrangement can substitute for the contract.

The research decision changes too. Evaluate memory as a set of causal capabilities: active-state control, event fidelity, generalisation and update, procedural transfer, selective forgetting and contamination resistance. Build negative controls that reproduce good recall for the wrong reason. Make failed episodes easy to inspect and impossible to execute as recipes.

The practical release gate is concise: no persistent record may influence a consequential action unless its type, scope, validity, provenance, outcome and applicable preconditions are inspectable. Current world state remains external. Authority remains deterministic. Effects remain independently verified.

That discipline does not make an agent remember as a human does. It makes the system modest about what each retained trace can support, which is the property production memory needs most.

The evidence conversationChart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The agent remembered the failure too well
  M->>E: The memory ecology is organised by temporal function
  E-->>M: compare rival explanations
  E->>D: Source ledger
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in Episodic, Semantic, Procedural and Working Memory. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Source ledger

Primary research: human and computational memory

  1. R. C. Atkinson and R. M. Shiffrin, “Human Memory: A Proposed System and its Control Processes”, 1968.
  2. A. D. Baddeley and G. J. Hitch, “Working Memory”, 1974.
  3. A. Baddeley, “The Episodic Buffer: A New Component of Working Memory?”, 2000.
  4. E. Tulving, “Episodic Memory: From Mind to Brain”, 2002.
  5. N. J. Cohen and L. R. Squire, “Preserved Learning and Retention of Pattern-Analyzing Skill in Amnesia”, 1980.
  6. J. L. McClelland, B. L. McNaughton and R. C. O’Reilly, “Why There Are Complementary Learning Systems in the Hippocampus and Neocortex”, 1995.
  7. L. R. Squire, “Memory Systems of the Brain: A Brief History and Current Perspective”, 2004.
  8. J. E. Laird, C. Lebiere and P. S. Rosenbloom, “A Standard Model of the Mind”, 2017.

Official and practitioner sources

  1. OWASP Cheat Sheet Series, “AI Agent Security Cheat Sheet”, current official guidance accessed during drafting.
  2. Rajesh Ranjan Mahapatra, “Governed Context Architecture: Reconstruction, Provenance and Active Memory for Long-Horizon Agents”, practitioner synthesis.
  3. Rajesh Ranjan Mahapatra, “Agent Memory as an Attack Surface: Provenance, Quarantine, Drift”, practitioner synthesis.