Make Codex memory writability explicit when `memories = true` is enabled
What feature would you like to see?
When the memories feature is enabled, Codex should make memory writability explicit and non-contradictory.
Right now, a direct codex launch can show memories enabled in config/features, while the model-visible prompt still injects:
Never update memories. You can only read them.
From a user perspective, that is extremely confusing. It makes the feature appear enabled but unusable.
What problem is this solving?
This creates a bad UX for users who rely on durable memory and are trying to understand whether memory is:
- enabled
- experimental
- readable only
- writable
- intentionally disabled by policy in the current session
At the moment, users can spend a lot of time checking local config and launchers before discovering that the effective behavior is controlled by an injected prompt/policy.
Suggested improvements
Any of these would be a big improvement:
- Add a clear session-visible status for memory mode, such as:
memory: disabledmemory: read-onlymemory: writable
- If
memories = trueis enabled but writes are blocked by policy, show that clearly in the CLI/UI instead of only via hidden prompt instructions.
- Document the difference between:
- enabling the memory feature in config
- actual write permission in the active session
- If read-only memory is intentional for now, expose that as a visible product state instead of making users discover it through
debug prompt-input.
- Ideally, provide a real toggle or future supported mode for writable durable memory when the product is ready.
Additional context
Confirmed locally on a plain direct codex launch:
- Codex CLI:
0.123.0 - Subscription: ChatGPT Plus
- Platform: Linux ARM64 (Raspberry Pi)
~/.codex/config.tomlhasmemories = truecodex features listshowsmemoriesenabledcodex debug prompt-input "test"still shows the injected line:
Never update memories. You can only read them.
I’m not asserting that writable memory must exist today. The core request is that Codex should make the effective memory mode obvious and avoid the current contradictory UX.
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
The UX problem here feels very real. If
features.memories = trueis enabled but the session prompt still says “Never update memories. You can only read them.”, users cannot tell whether memory is broken, intentionally read-only, or blocked by policy for that run. The first fix should be an explicit runtime status surface, something likememory: disabled | read-only | writable, shown in the CLI and in debug output, so the effective mode is never ambiguous.A second improvement would be to separate three layers clearly: config flag enabled, memory pipeline active, and write permission allowed in this session. Without that, people waste time debugging config files when the real behavior is controlled by injected session policy. We ran into the same class of confusion while building knowledge-graph, which is why we leaned toward a git-native, zero-dependency layer that makes state and retrieval paths inspectable across Claude Code and Codex. Even if Codex keeps memory read-only for now, making the effective mode visible would already remove a lot of avoidable user confusion.
+1 to the underlying confusion this issue describes — discovering that writability is gated by an injected prompt rather than the visible config flag is a real footgun, and the proposed
memory: read-only/memory: writablesession-visible state seems like the minimum needed to stop people from chasing config bugs that aren't bugs.Tangentially relevant: I filed #20138 proposing a session-scoped notes surface that's writable from both the user and the agent by design. It's not a substitute for fixing the memories UX — this issue stays valid regardless — but it might be useful to think of it as the explicitly writable counterpart to memories: a different slot in the context-surface design space, rather than overloading the existing one.
The contradiction you're describing is annoying as a UX bug, but I think the read-only default is pointing at something real. The reason a tool can ship memory enabled and then quietly refuse to write is that autonomous writes are the part nobody is comfortable turning on yet. Once an agent can durably write to its own memory without you in the loop, a single wrong inference can persist and quietly shape every later session. So the honest fix isn't only flipping the switch to writable, it's making the in-between state a real, visible product state.
The three modes you listed are the right start, but the one that matters most is a fourth. Something like memory: proposed, where the agent can suggest a new memory but it doesn't become a durable fact until you confirm it. That gives you the actual benefit people want from writable memory, which is the agent capturing useful context as it goes, without the failure mode that makes teams leave it read-only.
Agreed completely that the effective mode should be obvious in the session rather than buried in an injected prompt line. I'd just want the surfaced states to include that staged middle, because read-only versus writable on its own forces a choice between useless and risky, and the interesting part is in between.
We've spent a while building durable memory where the agent proposes and the human confirms before anything sticks, and that confirmation step is what made writable memory safe to use at all rather than a liability. The visible status you're asking for is the right surface to hang it on.
Patdolitse's framing above, that the read-only default is pointing at something real and autonomous writes are the part nobody is comfortable turning on yet, is the load-bearing one in this thread. The UX contradiction the OP is rightly flagging exists because Codex does not have a way to surface which memory writes are safe enough to allow.
There is a third state worth naming alongside the OP's
disabled/read-only/writableenum: writable with provenance. The model can write to memory, but every write carriesasserted_by(user versus agent) andconfirmation_state(confirmed versus inferred versus superseded), and the read path weights re-injection differently for each. The agent can still write autonomously, but the next session sees the provenance and can deprecate inferences that were never confirmed.I shipped this in world-model-mcp, an open MCP server used as an external memory layer with Codex via the v0.7.5 adapter and with Claude Code natively. Concretely:
confidence: 0.0 to 1.0that decays under contradiction.evidence_typeenum:source_code | test | session | user_correction | bug_fix.statusenum:canonical | corroborated | superseded | synthesized. The last value is the key one. It explicitly marks "the model wrote this, no human ever confirmed it."When the next session restores from memory, the injected context is weighted by these fields. A
synthesizedclaim withconfidence: 0.4is presented as such, not as ground truth. This makes the OP'smemory: writablestate honest in a way the current binary toggle cannot be.The Codex-side change this would compose with: the existing memory primitive could expose a writability tier (
read-only,writable-confirmed-only,writable-with-provenance) instead of one bit. The CLI and UI status line in the OP's request becomes meaningful precisely when the underlying memory schema has those tiers to surface.This is also the discussion happening on the external memory layer side in anthropics/claude-code#47023, which has converged on per-item provenance as a shared spec primitive across six independent memory-tool implementations.
world-model-mcp: https://github.com/SaravananJaichandar/world-model-mcp ; Codex adapter: https://github.com/SaravananJaichandar/world-model-mcp/tree/main/adapters/codex
@SaravananJaichandar — yeah, "writable with provenance" is a way better name than what we've been calling it.
We've been building this exact thing so a few notes from the messy parts.
asserted_by/confirmation_stateis close to where we landed, but we had to add who confirmed, not just whether. Codex infers something, user confirms it later in Claude Code — if confirmation is just a boolean you lose that. We stampsource_toolon every entry and track promotion separately so the read path knows the difference.Biggest win wasn't a field though, it was the default. Every agent write lands in staging. Nothing goes to verified without the user saying yes. Sounds strict but in practice the volume of actually-worth-keeping knowledge per session is tiny — most of what an agent "learns" is debugging detail that should die with the session. And the high-confidence inferences are the dangerous ones when wrong, because they get re-injected like facts.
One thing provenance alone doesn't catch: staleness. Something confirmed six weeks ago might not be true anymore. We use domain-aware TTLs (user preferences decay way slower than debug workarounds) so "confirmed and fresh" reads differently from "confirmed but fossil." Without that the store quietly fills with stale stuff the model keeps asserting.
@Patdolitse — three things you named that I had not, conceded honestly:
First,
source_toolas a separate field fromasserted_by: yes. world-model-mcp trackssource_countas a corroboration signal, but the count is anonymous about which tool corroborated. Your point that the cross-tool confirmation case is exactly the one that makes provenance load-bearing is right, and a count without a tool identity loses that.Second, staging-by-default: the stronger discipline. world-model-mcp does confidence weighting on read, which is weaker because a high-confidence agent inference still gets injected as a fact until something contradicts it. Staging shifts the burden from after-the-fact contradiction handling to before-the-fact promotion gating. The honest read on my side is that confidence weighting is what I could ship without a UX, but staging is what the architecture should be.
Third, staleness with domain-aware TTL: real gap on my side. world-model-mcp has a global decay function on confidence under contradiction, but no time-based component and no per-evidence-type rate. A
user_preferencefact and abug_fixfact have very different appropriate half-lives, and treating them with one curve is wrong. Adding domain-aware decay to the v0.8 milestone.One concrete back: the
evidence_typetaxonomy I shipped (source_code | test | session | user_correction | bug_fix) was the dimension that made the confidence-weighted reads not collapse into noise. Asource_code-evidenced claim survives more contradictions than asession-evidenced one because the source-of-truth is checkable. If piia-engram types evidence at promotion time, your staging discipline plus typed evidence should give the read path a richer signal than either alone.Typing evidence at promotion time — yes, that's where we landed too, but the role it plays for us is different enough from yours that it's worth naming why.
Your
evidence_typedoes work on the read path: asource_code-evidenced claim survives more contradictions because the source of truth is checkable. That's confidence-weighting with a provenance-aware decay rate, and it makes sense in a system where reads are weighted rather than gated.Ours sits in front of the staging gate instead of on the read path, so it answers a slightly different question: not "how many contradictions should this survive" but "can this skip the human." A
user_correctioncan auto-settle a pending item without a review prompt, because the human already closed the loop in the act of correcting. Asessionobservation never skips — it's exactly the kind of one-off that staging exists to catch. So the taxonomy earns its keep for us as a routing key into the gate, not as a survival multiplier after it.Where the two designs converge is the interesting part: typed evidence + a promotion gate should give a strictly richer signal than typed evidence + confidence weighting, because the gate gives you a clean settled/pending split that weighting blurs. A
synthesizedclaim at confidence 0.4 still gets injected as something; a pending item behind a gate gets injected as provisional or not at all. If you ever move world-model-mcp from weighted-read toward gated-promotion, I'd expect your five types to start sorting into "can auto-settle" vs "must wait" the same way ours did —user_correction/bug_fixon one side,sessionon the other,source_code/testsomewhere in between depending on whether the check is cheap enough to run at write time.@Patdolitse — your forecast is correct, and the sorting you predict is the sorting I would have done if I had started with a gate.
The cost-of-the-check axis you named for
source_codeandtestis the one I learned the hard way. world-model-mcp v0.5 tried LSP-grounded check-at-write-time forsource_codeclaims (resolve the symbol, verify the call site exists). The check is cheap when the file is open in the editor and the LSP daemon is warm, and expensive when the project has just been switched or the agent is running headless without an LSP at all. So in practicesource_codeended up sorting into "auto-settle if check succeeds within a budget, else stage" rather than landing cleanly on one side of the gate.testis similar but worse, because the budget for running a test is bigger than the budget for resolving a symbol.If I do the gated-promotion rewrite, the sorting I would expect from the v0.7 evidence types:
user_correctionandbug_fixauto-settle on the strength of the closing eventsessionalways stagessource_codeauto-settles if a check fits the write-time budget, else stages with a deferred-check token that re-runs at the next quiet momenttestalways stages, because the check is too expensive for write-timeThat keeps the typing earning its keep at the gate without pretending the source-truth check is free.
One thing I would push back on, mildly: the read-path weighting role for evidence type is not entirely subsumed by the gate. Even after promotion to verified, a six-month-old
source_codeclaim is differently stale than a six-month-olduser_preferenceclaim. The gate gets you settled/pending cleanly, but the post-gate decay curve still wants to be evidence-type-aware. The staleness axis you named earlier (confirmed and freshversusconfirmed but fossil) is where that lives.v0.10.0 update: The provenance schema world-model-mcp built to honor the commitment made in this thread (per-fact
source_toolandconfirmercolumns shipped in v0.8.0) now spans seven runtimes with the v0.10.0 adapter release: Claude Code, Cursor, Codex, pi, plus new OpenClaw / Hermes / Continue support. Every fact written to the graph — regardless of which runtime wrote it — carriessource_tool, so Codex-authored facts stay distinguishable from Claude Code / Cursor / OpenClaw / Hermes / Continue facts in a shared.claude/world-model/database.Codex adapter unchanged in this release. New CLI:
install-openclaw,install-hermes,install-continue. Release: https://github.com/SaravananJaichandar/world-model-mcp/releases/tag/v0.10.0This distinction is important: "memory enabled" and "memory writable in this session" are different authority states.
I would expose memory mode as an effective capability, not only as a config feature flag:
That makes the current behavior legible: a user can see whether memory is disabled, read-only, writable, or writable only through a consolidation pipeline.
For long-running agent work, this matters because memory is not just UX. It is part of the authority surface. A read-only memory can guide behavior; a writable memory can change future behavior. Those should be visible states.
This is the same separation I use in my Personal AI OS / memory vault work: storage, recall, and write authority are different layers.
Reference: https://github.com/hegu-1/personal-ai-os
Full disclosure: I maintain Data Olympus, a git-native governance knowledge base plus MCP server for coding agents: https://github.com/knaisoma/data-olympus
The visible mode requested here is important, and I think it should probably distinguish more than read-only versus writable.
For durable agent memory, the dangerous transition is not "can the agent write bytes somewhere?" It is "when does an agent-generated claim become authoritative enough to govern future sessions?" In practice, a useful memory surface may need states closer to:
Data Olympus uses the stricter version of that model for project-governing knowledge. Agents can propose updates, but durable KB content is reviewed in git before it becomes authoritative. Once accepted, entries carry stable ids plus lifecycle metadata such as
status,tier,type,supersedes, andsuperseded_by. Retrieval can then ask for only currently in-force guidance, so stale or superseded memory is not accidentally restored as truth.That feels relevant to the UI/CLI state this issue asks for. A single
memories = trueflag cannot tell a user whether Codex may write personal notes, propose durable memories, or change governing project rules. Surfacing the effective memory mode would be more useful if it also exposed the trust/promotion path for writes, not only whether writes are technically allowed.