From e1b58637183cba347b525332aede23c3089d61df Mon Sep 17 00:00:00 2001 From: Wendy Jiao Date: Thu, 12 Feb 2026 06:34:32 -0800 Subject: [PATCH 1/2] Add cwd to memory files --- codex-rs/core/src/memories/startup/dispatch.rs | 2 ++ codex-rs/core/src/memories/storage.rs | 4 ++++ codex-rs/core/src/memories/tests.rs | 3 +++ codex-rs/state/src/model/memories.rs | 5 +++++ codex-rs/state/src/runtime.rs | 3 +++ codex-rs/state/src/runtime/memories.rs | 4 ++++ 6 files changed, 21 insertions(+) diff --git a/codex-rs/core/src/memories/startup/dispatch.rs b/codex-rs/core/src/memories/startup/dispatch.rs index aaf21fed8a8e..6f084ff6f5d5 100644 --- a/codex-rs/core/src/memories/startup/dispatch.rs +++ b/codex-rs/core/src/memories/startup/dispatch.rs @@ -219,6 +219,7 @@ mod tests { use codex_state::Stage1Output; use codex_state::ThreadMetadataBuilder; use pretty_assertions::assert_eq; + use std::path::PathBuf; use std::sync::Arc; use tempfile::TempDir; @@ -337,6 +338,7 @@ mod tests { .expect("valid source_updated_at timestamp"), raw_memory: "raw memory".to_string(), rollout_summary: "rollout summary".to_string(), + cwd: PathBuf::from("/tmp/workspace"), generated_at: chrono::DateTime::::from_timestamp(124, 0) .expect("valid generated_at timestamp"), }; diff --git a/codex-rs/core/src/memories/storage.rs b/codex-rs/core/src/memories/storage.rs index a366623b9a2c..b60dd02b33fa 100644 --- a/codex-rs/core/src/memories/storage.rs +++ b/codex-rs/core/src/memories/storage.rs @@ -82,6 +82,8 @@ async fn rebuild_raw_memories_file(root: &Path, memories: &[Stage1Output]) -> st memory.source_updated_at.to_rfc3339() ) .map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?; + writeln!(body, "cwd: {}", memory.cwd.display()) + .map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?; writeln!(body) .map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?; body.push_str(memory.raw_memory.trim()); @@ -136,6 +138,8 @@ async fn write_rollout_summary_for_thread( memory.source_updated_at.to_rfc3339() ) .map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?; + writeln!(body, "cwd: {}", memory.cwd.display()) + .map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?; writeln!(body) .map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?; body.push_str(&memory.rollout_summary); diff --git a/codex-rs/core/src/memories/tests.rs b/codex-rs/core/src/memories/tests.rs index 5f57eb3cf2c1..eb36eed6b62f 100644 --- a/codex-rs/core/src/memories/tests.rs +++ b/codex-rs/core/src/memories/tests.rs @@ -11,6 +11,7 @@ use codex_protocol::ThreadId; use codex_state::Stage1Output; use pretty_assertions::assert_eq; use serde_json::Value; +use std::path::PathBuf; use tempfile::tempdir; #[test] @@ -97,6 +98,7 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only source_updated_at: Utc.timestamp_opt(100, 0).single().expect("timestamp"), raw_memory: "raw memory".to_string(), rollout_summary: "short summary".to_string(), + cwd: PathBuf::from("/tmp/workspace"), generated_at: Utc.timestamp_opt(101, 0).single().expect("timestamp"), }]; @@ -115,4 +117,5 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only .expect("read raw memories"); assert!(raw_memories.contains("raw memory")); assert!(raw_memories.contains(&keep_id)); + assert!(raw_memories.contains("cwd: /tmp/workspace")); } diff --git a/codex-rs/state/src/model/memories.rs b/codex-rs/state/src/model/memories.rs index 81a7354c5401..aff702d673e7 100644 --- a/codex-rs/state/src/model/memories.rs +++ b/codex-rs/state/src/model/memories.rs @@ -4,6 +4,7 @@ use chrono::Utc; use codex_protocol::ThreadId; use sqlx::Row; use sqlx::sqlite::SqliteRow; +use std::path::PathBuf; use super::ThreadMetadata; @@ -14,6 +15,7 @@ pub struct Stage1Output { pub source_updated_at: DateTime, pub raw_memory: String, pub rollout_summary: String, + pub cwd: PathBuf, pub generated_at: DateTime, } @@ -23,6 +25,7 @@ pub(crate) struct Stage1OutputRow { source_updated_at: i64, raw_memory: String, rollout_summary: String, + cwd: String, generated_at: i64, } @@ -33,6 +36,7 @@ impl Stage1OutputRow { source_updated_at: row.try_get("source_updated_at")?, raw_memory: row.try_get("raw_memory")?, rollout_summary: row.try_get("rollout_summary")?, + cwd: row.try_get("cwd")?, generated_at: row.try_get("generated_at")?, }) } @@ -47,6 +51,7 @@ impl TryFrom for Stage1Output { source_updated_at: epoch_seconds_to_datetime(row.source_updated_at)?, raw_memory: row.raw_memory, rollout_summary: row.rollout_summary, + cwd: PathBuf::from(row.cwd), generated_at: epoch_seconds_to_datetime(row.generated_at)?, }) } diff --git a/codex-rs/state/src/runtime.rs b/codex-rs/state/src/runtime.rs index c8ee8fdba003..77930f800105 100644 --- a/codex-rs/state/src/runtime.rs +++ b/codex-rs/state/src/runtime.rs @@ -2093,8 +2093,10 @@ WHERE kind = 'memory_stage1' assert_eq!(outputs.len(), 2); assert_eq!(outputs[0].thread_id, thread_id_b); assert_eq!(outputs[0].rollout_summary, "summary b"); + assert_eq!(outputs[0].cwd, codex_home.join("workspace-b")); assert_eq!(outputs[1].thread_id, thread_id_a); assert_eq!(outputs[1].rollout_summary, "summary a"); + assert_eq!(outputs[1].cwd, codex_home.join("workspace-a")); let _ = tokio::fs::remove_dir_all(codex_home).await; } @@ -2163,6 +2165,7 @@ VALUES (?, ?, ?, ?, ?) assert_eq!(outputs.len(), 1); assert_eq!(outputs[0].thread_id, thread_id_non_empty); assert_eq!(outputs[0].rollout_summary, "summary"); + assert_eq!(outputs[0].cwd, codex_home.join("workspace-non-empty")); let _ = tokio::fs::remove_dir_all(codex_home).await; } diff --git a/codex-rs/state/src/runtime/memories.rs b/codex-rs/state/src/runtime/memories.rs index 19f277c82f9b..4d1f6ffd59b9 100644 --- a/codex-rs/state/src/runtime/memories.rs +++ b/codex-rs/state/src/runtime/memories.rs @@ -178,6 +178,7 @@ LEFT JOIN jobs /// /// Query behavior: /// - filters out rows where both `raw_memory` and `rollout_summary` are blank + /// - joins `threads` to include thread `cwd` /// - orders by `source_updated_at DESC, thread_id DESC` /// - applies `LIMIT n` pub async fn list_stage1_outputs_for_global( @@ -191,7 +192,10 @@ LEFT JOIN jobs let rows = sqlx::query( r#" SELECT so.thread_id, so.source_updated_at, so.raw_memory, so.rollout_summary, so.generated_at + , COALESCE(t.cwd, '') AS cwd FROM stage1_outputs AS so +LEFT JOIN threads AS t + ON t.id = so.thread_id WHERE length(trim(so.raw_memory)) > 0 OR length(trim(so.rollout_summary)) > 0 ORDER BY so.source_updated_at DESC, so.thread_id DESC LIMIT ? From 26e81394222791c835d075c7f27bfa90ac606081 Mon Sep 17 00:00:00 2001 From: Wendy Jiao Date: Thu, 12 Feb 2026 08:35:32 -0800 Subject: [PATCH 2/2] update prompt --- codex-rs/core/templates/memories/consolidation.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/templates/memories/consolidation.md b/codex-rs/core/templates/memories/consolidation.md index 6bd9230feefb..de0965082b43 100644 --- a/codex-rs/core/templates/memories/consolidation.md +++ b/codex-rs/core/templates/memories/consolidation.md @@ -97,6 +97,8 @@ Primary inputs (always read these, if exists): Under `{{ memory_root }}/`: - `raw_memories.md` - mechanical merge of `raw_memories` from Phase 1; + - source of rollout-level metadata needed for MEMORY.md header annotations; + you should be able to find `cwd` and `updated_at` there. - `MEMORY.md` - merged memories; produce a lightly clustered version if applicable - `rollout_summaries/*.md` @@ -129,8 +131,8 @@ Rules: Clustered schema: --- rollout_summary_files: - - () - - () + - () + - () description: brief description of the shared tasks/outcomes keywords: k1, k2, k3, ... --- @@ -141,6 +143,9 @@ keywords: k1, k2, k3, ... ` and `updated_at=` copied from that rollout summary metadata. + If missing from an individual rollout summary, recover them from `raw_memories.md`. - If you need to reference skills, do it in the BODY as bullets, not in the header (e.g., "- Related skill: skills//SKILL.md"). - Use lowercase, hyphenated skill folder names. @@ -338,4 +343,4 @@ Use `rg` for fast retrieval while consolidating: - Search across memory tree: `rg -n -i "" "{{ memory_root }}" | head -n 50` - Locate rollout summary files: - `rg --files "{{ memory_root }}/rollout_summaries" | head -n 200` \ No newline at end of file + `rg --files "{{ memory_root }}/rollout_summaries" | head -n 200`