Skip to content

Memory read path ignores cwd and injects entire global memory_summary.md into initial context for new sessions #17496

@grp06

Description

@grp06

What issue are you seeing?

Codex's memory write path is cwd-aware, but the memory read/injection path is not.

Today, phase 2 consolidation explicitly preserves cwd/project boundaries in the memory artifacts:

  • MEMORY.md blocks require applies_to: cwd=... in the schema in codex-rs/core/templates/memories/consolidation.md:197
  • the consolidation instructions say to separate similar tasks across different cwd contexts by default in codex-rs/core/templates/memories/consolidation.md:293
  • the summary/index is organized first by cwd/project scope in codex-rs/core/templates/memories/consolidation.md:546

However, when Codex builds the initial context for a fresh conversation / baseline, the runtime prompt injection path reads and injects the entire global ~/.codex/memories/memory_summary.md without any cwd/project filtering:

  • the initial context builder appends the memory developer prompt in codex-rs/core/src/codex.rs:3764
  • build_memory_tool_developer_instructions(...) reads the whole memory_summary.md in codex-rs/core/src/memories/prompts.rs:234
  • the read prompt injects {{ memory_summary }} wholesale in codex-rs/core/templates/memories/read_path.md:124
  • the full initial context is used when reference_context_item.is_none() in codex-rs/core/src/codex.rs:3928

Steady-state turns appear to use the settings-diff path instead of rebuilding the full initial context, so this is specifically an initial-context / new-conversation boundary issue rather than a claim that the full memory summary is re-injected on every turn.

This still creates a real failure mode for multi-repo users: a fresh session in one repo gets irrelevant memory from other repos injected into context before the model starts working. That is both noisy and architecturally inconsistent with the cwd-aware write/consolidation design.

Concretely, I have a generated memory_summary.md that contains both /Users/georgepickett/polyscraper and unrelated /Users/georgepickett/explore-computer sections. A fresh polyscraper session would get both injected, even though only the polyscraper section is relevant.

This also makes multiple memories_extensions/*/instructions.md files indirectly compete over one shared global summary: phase 2 reads all extension instructions and rewrites one global memory_summary.md, but the runtime still injects that single shared summary into fresh initial context everywhere.

Why this matters:

  • irrelevant context can bias the model toward the wrong workflow
  • unrelated memory competes for the limited injected summary budget
  • the injected summary is truncated to 5,000 tokens in codex-rs/core/src/memories/mod.rs:46, so unrelated repo memory can crowd out the repo that actually matters
  • the system is storing project boundaries in the data model but not respecting them in retrieval/injection

What steps can reproduce the bug?

  1. Enable memories and allow the startup memory pipeline to run. The startup trigger is in codex-rs/core/src/memories/start.rs:14.
  2. Use Codex across at least two distinct repos/cwds so phase 1 and phase 2 build cross-project memory.
  3. Let phase 2 consolidate those memories into a shared ~/.codex/memories/memory_summary.md. Phase 2's global consolidation pass is in codex-rs/core/src/memories/phase2.rs:80.
  4. Confirm that memory_summary.md now contains multiple cwd/project sections.
  5. Start a new top-level session in one repo (for example, /Users/georgepickett/polyscraper).
  6. Observe from the code path that the initial context builder reads the entire memory_summary.md and injects it without filtering to the current cwd/project.

This is easiest to see from the code rather than a single UI screenshot:

  • injection gate: codex-rs/core/src/codex.rs:3764
  • full-file read: codex-rs/core/src/memories/prompts.rs:234
  • prompt template includes the whole summary: codex-rs/core/templates/memories/read_path.md:124
  • fresh baseline path: codex-rs/core/src/codex.rs:3928

What is the expected behavior?

The read path should respect the same repo/cwd boundary that the write/consolidation path is already trying to preserve.

A better architecture would be a hybrid model:

  • maintain a tiny global user profile for genuinely cross-project preferences
  • maintain per-repo/per-cwd memory_summary and MEMORY artifacts for repo-specific workflow, status, runbooks, and history
  • on a new session / fresh baseline, inject:
    • the tiny global profile, plus
    • only the matching repo/cwd summary for the current session
  • keep repo-specific detailed memory (MEMORY.md, rollout summaries, skills) scoped to that repo/cwd for later lookup

At minimum, build_memory_tool_developer_instructions(...) should filter or select the relevant section(s) of the summary by current cwd/project before injection, instead of pasting the entire global file.

Additional information

A few related design tensions surfaced while tracing this locally:

  • Phase 2 reads all memories_extensions/*/instructions.md files in the consolidation prompt in codex-rs/core/src/memories/prompts.rs:55, so multiple extensions currently influence one shared memory_summary.md.
  • Because the runtime injects one global summary into fresh initial context, those extensions indirectly compete for a single prompt budget over time.
  • This makes instructions.md a mitigation for summary quality, but not a real fix for retrieval/injection scope.

Suggested implementation directions:

  • add cwd/project-aware selection inside build_memory_tool_developer_instructions(...)
  • or split artifacts into:
    • global profile summary
    • per-repo/per-cwd summary
    • per-repo/per-cwd detailed memory
  • use current cwd/project root as the primary routing key, with global profile as a fallback/overlay

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentIssues related to the core agent loopbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions