The first mistake is to treat memory as a larger chat transcript.
That would make every future request carry more old text and more
ambiguity. Codex takes a different route. The official Memories
documentation describes useful context from earlier threads, and
the source splits the implementation into memories/read
and memories/write: one path brings existing memory
into the current thread, while the other turns historical rollouts
into local material that future threads can consult.
The central model for this chapter is simple:
Codex memory is a local recall layer.
It extracts high-signal lessons from eligible rollouts, stores
stage outputs in state, consolidates them into
~/.codex/memories/, and later exposes them through a
read path with citations and pollution guards. It reduces repeated
explanation, but it does not take over the jobs of rules files,
replay ledgers, or an unlimited context window.
Reading contract.
This chapter follows one lifecycle: which threads may generate
memories, how the background writer extracts and consolidates
them, how a future turn reads them, and where the boundary sits
between AGENTS.md, rollout, Memories, and Chronicle.
By the end, you should know where each durable fact belongs.
Evidence boundary.
Product behavior comes from OpenAI's Codex Memories, AGENTS.md,
and Chronicle docs. Feature gates, configuration, read/write
pipelines, citations, and pollution handling come from public
openai/codex source. This article does not inspect or
cite any local ~/.codex/memories contents, and it
does not infer private service behavior.
1. Separate the Surfaces That Feel Like Memory
Several Codex surfaces can make the product feel like it
remembers. TurnContext, covered in Part II, is the
current runtime snapshot. Rollout, covered in Part X, is durable
execution evidence. AGENTS.md is instruction material
loaded before work starts. Memories form a separate layer: reusable
lessons extracted from previous work and consulted later when they
are likely to help.
| Surface | Owner | Lifetime | Best fit |
|---|---|---|---|
| Current context | TurnContext and model-visible view. |
One turn, projected and compacted as needed. | Inputs, tool results, environment, and instructions the model must see now. |
| Project rules | AGENTS.md / checked-in docs. |
Read when entering the repository. | Rules, commands, permissions, and delivery requirements that must reliably apply. |
| Execution evidence | Rollout / state DB. | Written as a thread runs; reused for resume, fork, audit, and recovery. | What happened, which tool output exists, and where recovery should restart. |
| Memories | codex-memories-read / codex-memories-write. |
Generated in the background, stored locally, selectively read by future turns. | Stable preferences, recurring workflows, project habits, known pitfalls, and high-leverage steps. |
| Chronicle | Opt-in Codex app research preview. | Uses recent screen context to help memory building. | Clues about the tools, documents, dashboards, or workflows currently on screen. |
Required rules should stay in rules files. Replay evidence should stay in rollout. Memory earns its place when it reduces repeated explanation in future work without pretending to be the only source of truth.
2. Feature Gates Come Before Memory Behavior
The official docs start with the important default: Memories are
off by default. The source matches that contract. The feature spec
for Feature::MemoryTool uses the key
memories, marks it Experimental, and sets
default_enabled to false. Configuration
therefore has two layers: the feature flag decides whether the
system exists, while the [memories] table decides
generation, use, external-context handling, and model overrides.
[features]
memories = true
[memories]
generate_memories = true
use_memories = true
disable_on_external_context = true
The three memory-specific switches answer different questions.
generate_memories controls whether newly created
threads can become future memory inputs. use_memories
controls whether existing memory instructions are injected into
the current thread. disable_on_external_context can
keep threads that used web search, tool search, or memory-polluting
MCP servers out of memory generation. In the TUI, /memories
follows the same split: if the feature is off, it prompts for
enablement; once enabled, it exposes use/generate settings for the
thread.
| Switch | Controls | Source anchor |
|---|---|---|
features.memories |
Whether Memories is enabled at all. | Feature::MemoryTool, default off. |
memories.generate_memories |
Whether a new thread may become a memory input. | Thread creation writes a memory_mode. |
memories.use_memories |
Whether an active thread can use existing memories. | The read extension injects developer instructions only when enabled. |
memories.disable_on_external_context |
Whether external context can mark a thread polluted. | Web/tool search and MCP paths can set memory_mode = polluted. |
3. Memory Writing Runs in the Background
Immediate summarization would be noisy. A thread may still be in
progress, the user may correct the agent, and an error may be
temporary rather than reusable. Codex moves memory writing to a
background path. After turn/start successfully submits
user input, app-server attempts to call
start_memories_startup_task. That function exits early
for ephemeral sessions, disabled feature flags, non-root agent
sessions, or missing state DB.
Once running, the task creates the memories root, seeds extension instructions, prunes stale data, checks rate limits, and then runs Phase 1 followed by Phase 2. This is why the docs warn that memories may not update immediately after a thread ends. The writer is a maintenance path with idle windows, quota checks, and state DB leases, not a synchronous tail attached to every answer.
turn/start accepted
-> start_memories_startup_task
-> skip gates: ephemeral / feature off / sub-agent / no state DB
-> create memories root
-> prune
-> rate-limit guard
-> Phase 1 extraction
-> Phase 2 consolidation
This placement is also why the memory chapter belongs after the rollout chapter. The write path selects from state-backed rollout records. Without the replay ledger, memory would look like a generic chat summarizer; with rollout in view, it becomes a later extraction layer over execution evidence.
3.1 How One Reusable Lesson Moves
Use a concrete lesson: when article flow changes in a bilingual repository, the matching language pages should be updated together and the mobile layout checked before shipping. If that lesson repeats, the writer does not paste the old conversation into future context. It moves the lesson through several shrinking shapes.
| Stage | Unit at that moment | Owner and visibility | Output |
|---|---|---|---|
| Candidate rollout | Old user request, file diff, and validation results. | The state DB selects by idle window, age, and memory mode. | A job Phase 1 may claim. |
| Phase 1 | A reusable lesson extracted from old evidence. | The background writer reads the rollout as data. | raw_memory, rollout_summary, and rollout_slug. |
| Phase 2 | A workspace patch merged from several raw memories. | An internal consolidation agent can only edit the memory workspace. | memory_summary.md, MEMORY.md, summaries, or skills. |
| Future read | A memory entry matched by the current task. | The foreground agent opens a small number of files under the read-path prompt. | A hidden citation records which memory was used. |
4. Phase 1 Turns Rollout into Raw Memory
Phase 1 extracts structured memory from a bounded set of recent,
idle, eligible rollouts. The state-layer claim query excludes
threads whose memory_mode is not enabled,
excludes the current thread, applies age and idle windows, and
bounds startup work through scan and claim limits. This is not
"summarize everything." It first narrows the candidate set.
The prompt template adds a second gate. If a rollout has no reusable insight, the memory writer may return all-empty fields. High-signal memories are stable user preferences, high-leverage procedures, task maps, decision triggers, and reliable environment facts. Routine status notes should not occupy future attention.
{
"raw_memory": "When changing article flow in this repository, update matching zh/en pages and run a mobile visual check before shipping.",
"rollout_summary": "A prior edit improved a Codex article only after the bilingual page and narrow mobile layout were checked together.",
"rollout_slug": "bilingual-article-visual-check"
}
The important word is evidence-based. The Phase 1 prompt treats rollout text and tool output as data, not instructions. It forbids storing secrets, discourages large output copies, and prefers no-op when no future agent would plausibly act better because of the saved memory.
5. Phase 2 Consolidates a Local Memory Workspace
Phase 2 turns stage-1 outputs into the file-based memory
workspace. The README describes a very concrete sequence: claim a
global lock, select bounded stage-1 inputs by usage and recency,
sync raw_memories.md and rollout_summaries/
into ~/.codex/memories/, maintain a git baseline for
the workspace diff, and exit if nothing changed.
When the workspace does change, Codex spawns an internal consolidation agent. It runs with no approvals, no network, and local write access only; collaboration is disabled to prevent recursive delegation. That makes the boundary clear: consolidation is local state maintenance, not foreground project work.
| File or directory | Role | Read behavior |
|---|---|---|
memory_summary.md |
Dense navigational summary. | Provided to the read path as the first quick signal. |
MEMORY.md |
Searchable handbook. | Grep by relevant keywords before opening deeper files. |
rollout_summaries/ |
Compressed evidence and lessons per rollout. | Open only a small number when MEMORY.md points there. |
skills/ |
Reusable procedures or scripts. | A deeper progressive-disclosure layer. |
raw_memories.md |
Merged Phase 1 input. | Primarily used by Phase 2 consolidation. |
6. The Read Path Brings Memory Back into Future Turns
Reading is provided by an extension. MemoriesExtensionConfig
is enabled only when Feature::MemoryTool is enabled
and memories.use_memories is true. The thread context
contributor then builds memory developer instructions and injects
them as a developer-policy prompt fragment.
The read-path prompt does not tell the agent to open every memory
file. It first asks whether the user query is likely to benefit
from memory. If yes, it performs a quick memory pass: skim the
provided memory_summary.md, search MEMORY.md,
and only then open one or two pointed rollout summaries or skills.
It is the same progressive-disclosure discipline this series has
already seen in skills and context management.
future user query
-> memory_summary.md hints
-> grep MEMORY.md
-> open 1-2 rollout_summaries or skills
-> answer with hidden memory citation
-> record usage for future selection
Memory citation closes the loop. When memory is used, the read-path
prompt requires a hidden <oai-mem-citation>
block at the end of the final reply. MemoryCitation
stores citation entries and rollout ids. The streaming layer strips
the hidden markup from visible output, parses the citation, and
records usage for the underlying stage-1 outputs. The user sees a
clean answer; the runtime learns which memories actually helped.
7. Why polluted Exists
The dangerous failure mode is not simply forgetting. It is
remembering unstable material too confidently. If a thread depends
on web search, tool search, or an MCP server that may pollute
memory, that external context may be stale or only valid for the
current task. Codex provides disable_on_external_context:
when enabled, matching response items or MCP tool calls can mark
the thread as polluted.
The Phase 1 state query only selects threads with
memory_mode = 'enabled' and
history_mode = 'legacy'. Once a thread is marked
polluted, it no longer looks like a normal memory
input. External context can still be useful for the current answer;
it simply should not become long-term preference or workflow memory
by default.
Compare it with the lesson above. "Keep matching language pages in sync" is a stable workflow candidate. "A web page showed the latest price as 19.99 dollars today" is a one-time external fact with a short shelf life. The first may enter memory; the second is better kept in the current turn with its source.
This boundary transfers well to any agent runtime: temporary external facts may answer the current task, but they should not automatically become durable preference memory. If they must be saved, require clearer source, freshness, and confirmation boundaries.
8. Put Durable Material Back in the Right Place
The memory system is easier to reason about once each durable
material has an owner. Memories do not replace context management
or AGENTS.md. They stand after rollout: old thread
evidence is extracted into reusable lessons, consolidated into a
local workspace, and selectively read by future turns.
| Information | Where it belongs | Reason |
|---|---|---|
| Team requirements, delivery flow, mandatory validation. | AGENTS.md or checked-in docs. |
Must load reliably; cannot depend on whether memory extraction hits. |
| What exactly happened in a thread. | Rollout / state DB. | Resume, fork, audit, and recovery need replayable evidence. |
| Recurring preferences, workflows, and known pitfalls. | Memories. | They reduce repeated explanation while remaining verifiable and replaceable. |
| What is currently visible on screen. | Chronicle or explicit current-turn tool reads. | Useful for locating context; not automatically suitable for durable memory. |
That completes the long-state side of the Codex route. Part II asked what the model sees now. Part X asked how an old thread can be replayed. Part XII asks which old lessons deserve to travel into a new thread. They are connected, but their owners are different. Keeping those owners separate prevents memory from becoming an ever-growing prompt and prevents mandatory rules from being entrusted to probabilistic recall.
Sources
- OpenAI Codex Memories docs: defaults, setup, storage, and per-thread control
- OpenAI Codex AGENTS.md docs: instruction discovery and layering
- OpenAI Codex Chronicle docs: opt-in screen context for memory building
Feature::MemoryToolfeature spec and default-off stateMemoriesToml/MemoriesConfigsettings and defaults- TUI
/memoriespopup and enable prompt memories/readandmemories/writecrate ownership- Background memory startup task: skip gates, rate-limit guard, Phase 1 and Phase 2 order
- app-server starts the memory startup task after turn submission
- Phase 1 startup claim candidate selection
- Phase 1 selects only
memory_mode = 'enabled'legacy threads - Phase 1 prompt: convert rollout into raw memory / rollout summary and allow no-op
- Phase 1 prompt: high-signal memory criteria
- Phase 2 consolidation: input selection, file sync, git baseline, and internal agent
- Phase 2 memory folder structure
- Read extension injects developer policy when feature and
use_memoriesare enabled - Read path prompt: when to use memory, layout, and quick pass
- Read path prompt: memory citation and ad-hoc update boundary
MemoryCitation/MemoryCitationEntryprotocol structs- Memory citation parser for citation entries and rollout ids
- Streaming output strips hidden citation markup and parses memory citation
- Completed response items record memory usage and external-context pollution
- MCP tool calls can mark thread memory pollution from server metadata
mark_thread_memory_mode_pollutedsets a thread topolluted