feat: enable per-project Codex memories - #79
Conversation
Codex sessions start from a fresh container, so without memories every session relearns the project from scratch. Turn them on in the settings template and give the spec schema the controls this needs: - sandbox.memoryScope: session couples the memory mount to the config-store key, so concurrent stores never share a memory database - sandbox.noMemoryArgs natively disables memory use and generation for --no-memory and --ephemeral runs; Claude declares it too, so those flags now stop generation instead of only omitting the mount - sandbox.statePaths pins config-relative runtime state across config-source overlays and keeps it out of host config passthrough; Codex pins its memory files, their index database, and its thread databases (rebuildable rollout projections, pinned to avoid re-deriving them on every overlaid run) - cleanup treats session-scoped memory and its config store as one unit for --keep memory/--keep history and tolerates removed tools - tools add/update report the new grants in the capability summary and update diff
c43efe2 to
e6b98ff
Compare
EclipseSourceAI
left a comment
There was a problem hiding this comment.
Note
Autonomous AI review.
This review was done by an AI agent and therefore may contain mistakes. Feel free to ignore any comment you disagree with. A thumbs-down reaction on a comment marks it as rejected for follow-up reviews. Noting why in a reply helps, since replies are read too.
Resolving all AI comments does not lead to an automatic approval. A maintainer still needs to review and sign off on the overall architecture and design.
To get an updated review after pushing changes, a maintainer may re-request a review from this account.
Running in Eclipse Enclave, submitted via review-guard-mcp
Enables Codex memories (features.memories = true in the settings template) and generalizes enclave's memory model to fit a tool that splits memory between the bind mount and its config store. Three new spec-schema fields carry that: sandbox.memoryScope: session keys the memory mount by config-store key (reusing the existing sessionGeneratedKey, same as the skills mount), sandbox.noMemoryArgs gives --no-memory/--ephemeral a native disable argv (Claude declares one too, so those flags now stop generation instead of only dropping the mount), and sandbox.statePaths pins config-relative runtime state, routed into the existing HostConfigPassthroughDeniedPaths so both the overlay preserve list and passthrough denial pick it up for free. Cleanup learns to treat session memory and its config store as one unit and to tolerate tools whose extension is gone.
The integration looks clean: existing seams get reused rather than duplicated, compactProfileArgs moves to load-time normalization where it also covers the new argv, and the docs (stores.md, extension docs, cli-reference) are updated properly. Tests are thorough, including the overlay running against the real preserve-path policy. Build, vet, and the touched package tests pass locally.
Points worth a maintainer's attention:
- Turning
memories = trueon by default consumes plan quota for every existing Codex user on upgrade. A product call, not a code issue. - Cleanup's abort-vs-warn split on an unloadable spec is fine for the single-tool path but hard-fails the whole
--ephemeral --allsweep. statePathspinsthread-writer-locks/, which is the one entry where preserving state across an overlay could carry a stale lock forward.- The permanent-opt-out documentation omits enclave's own
no_memoryconfig option.
Remaining inline comments are small validation and documentation nits.
Review feedback on #79: - one broken third-party spec no longer aborts an --ephemeral --all sweep for every other tool and project: the sweep warns and falls back to the default scope per tool, and under --keep memory skips the broken tool's stores, since the scope decides what would be kept - reject memory_scope declared without memory_dir, matching how the session branch and skills_dir treat their dependent fields - codex spec: note that statePaths are config-relative unlike the home-relative memoryDir, and that codex reconciles stale writer locks itself, so pinning the lock directory is safe - codex README: list the no_memory config option as an opt-out
EclipseSourceAI
left a comment
There was a problem hiding this comment.
Note
Autonomous AI review.
This review was done by an AI agent and therefore may contain mistakes. Feel free to ignore any comment you disagree with. A thumbs-down reaction on a comment marks it as rejected for follow-up reviews. Noting why in a reply helps, since replies are read too.
Resolving all AI comments does not lead to an automatic approval. A maintainer still needs to review and sign off on the overall architecture and design.
To get an updated review after pushing changes, a maintainer may re-request a review from this account.
Running in Eclipse Enclave, submitted via review-guard-mcp
Follow-up review. The new commit (a76a6d7) addresses all four inline points from my earlier review: memory_scope: project without memory_dir is now rejected, the ephemeral sweep no longer aborts entirely on one unloadable spec (falls back per tool, skipping only under --keep memory), the codex spec comment now explains the config-relative base and why pinning the lock dir is safe, and the README lists no_memory as an opt-out. Tests were updated to match (cleanup_test.go, spec_loader_test.go) and pass locally, along with go build ./....
The default-on features.memories = true in config.toml (quota impact for existing users) is unchanged, but that was flagged as a product/maintainer sign-off question, not a code defect, so nothing to add there.
No new findings in the delta since my last review.
These previous comments can be resolved as they are now handled:
- reject
memory_scope: projectdeclared withoutmemory_dir - stop one broken spec from aborting the whole
--ephemeral --allsweep - explain why pinning codex's writer-lock dir across an overlay is safe
- clarify statePaths are config-relative unlike the home-relative memoryDir
- list enclave's own
no_memoryoption as a memory opt-out
I can't resolve them myself as I would need write permission on this repository.
xai
left a comment
There was a problem hiding this comment.
Code looks good and the feature works fine!
What it does
Turns on Codex memories (
[features] memories = truein the settings template) so sandboxed sessions stop relearning the project from scratch. Background consolidation uses plan quota; opt out per run with--no-memoryor permanently with a config patch.The existing memory system assumed Claude's model (self-contained files in one mounted directory). Codex splits memory between the mount and its config store, which drives the new spec-schema controls (available to all tool extensions, including installed third-party ones):
sandbox.memoryScope: sessionkeys the memory mount by config-store key, so concurrent stores never share a memory databasesandbox.noMemoryArgsnatively disables memory use and generation for--no-memoryand--ephemeralruns. Claude declares it too (--settings '{"autoMemoryEnabled": false}'), so those flags now stop generation instead of only omitting the mountsandbox.statePathspins config-relative runtime state across config-source overlays and keeps it out of host config passthrough. Codex pins its memory files, their index database, and its thread databases; the thread databases are rebuildable rollout projections, pinned to avoid re-deriving them on every overlaid runcleanuptreats session-scoped memory and its config store as one unit (--keep memory/--keep history) and tolerates tools whose extension has been removedtools add/updatereport the new grants (agent memory, no-memory args, preserved state paths) in the capability summary and update diffMemory scope is only resolved for tools that declare a memory dir, so memory-less tools don't grow a meaningless field in their golden surface.
Fixes #78
How to test
make build && make test && make lint
All probes run inside
enclave --tool codex shellsessions; no host paths needed. Planted markers stand in for real memory generation, which the model triggers at its own discretion.echo probe > ~/.codex/memories/PROBE.md, exit. Start a new shell session: the probe is still there.--no-memory: the probe is gone, because it lives in the omitted memory mount rather than the config store. A regular restart shows it again; remove it afterwards.~/.config/enclave/patches/codex/config.toml), in a shell sessiontouch ~/.codex/state_999.sqlite ~/.codex/memories_999.sqlite, exit, restart: both markers survive the store overlay. On main the overlay deletes them.--ephemeralshell session write a probe into~/.codex/memories/, exit, start a regular session: the probe is gone, discarded with the session store.enclave cleanup --tool codex --dry-run --keep memoryretains the config store together with memory;--keep historyretains both as well; a plaincleanup --dry-runlists both for removal.--no-memoryCodex session reports memories disabled via/memories. The injected argv (-c features.memories=false, Claude's--settings '{"autoMemoryEnabled": false}') is pinned by unit tests.The same seams are covered by automated tests (
memory_mount_test.go,overlay_test.go,cleanup_test.go,host_config_passthrough_test.go); the probes are the end-to-end variant of those assertions.Follow-ups
$TMPDIRand walks into directories it already staged wholesale. WithstatePathsnow pinning large trees (memories/can hold a git repository), staging via rename on the same filesystem and skipping staged directories would cut startup cost on overlaid runs. Worth a ticket; not done here to keepoverlay.goout of this PR.Breaking changes
Review checklist