Add rollout path to memory files and search for them during read (#12684)

Co-authored-by: jif-oai <jif@openai.com>
This commit is contained in:
Wendy Jiao
2026-02-26 10:57:01 +00:00
committed by GitHub
co-authored by jif-oai
parent 6acede5a28
commit 52aa49db1b
6 changed files with 39 additions and 7 deletions
+5
View File
@@ -82,6 +82,8 @@ async fn rebuild_raw_memories_file(
)
.map_err(raw_memories_format_error)?;
writeln!(body, "cwd: {}", memory.cwd.display()).map_err(raw_memories_format_error)?;
writeln!(body, "rollout_path: {}", memory.rollout_path.display())
.map_err(raw_memories_format_error)?;
let rollout_summary_file = format!("{}.md", rollout_summary_file_stem(memory));
writeln!(body, "rollout_summary_file: {rollout_summary_file}")
.map_err(raw_memories_format_error)?;
@@ -138,6 +140,8 @@ async fn write_rollout_summary_for_thread(
memory.source_updated_at.to_rfc3339()
)
.map_err(rollout_summary_format_error)?;
writeln!(body, "rollout_path: {}", memory.rollout_path.display())
.map_err(rollout_summary_format_error)?;
writeln!(body, "cwd: {}", memory.cwd.display()).map_err(rollout_summary_format_error)?;
writeln!(body).map_err(rollout_summary_format_error)?;
body.push_str(&memory.rollout_summary);
@@ -267,6 +271,7 @@ mod tests {
raw_memory: "raw memory".to_string(),
rollout_summary: "summary".to_string(),
rollout_slug: rollout_slug.map(ToString::to_string),
rollout_path: PathBuf::from("/tmp/rollout.jsonl"),
cwd: PathBuf::from("/tmp/workspace"),
generated_at: Utc.timestamp_opt(124, 0).single().expect("timestamp"),
}
+18 -1
View File
@@ -86,6 +86,7 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
raw_memory: "raw memory".to_string(),
rollout_summary: "short summary".to_string(),
rollout_slug: None,
rollout_path: PathBuf::from("/tmp/rollout-100.jsonl"),
cwd: PathBuf::from("/tmp/workspace"),
generated_at: Utc.timestamp_opt(101, 0).single().expect("timestamp"),
}];
@@ -135,6 +136,7 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
assert!(raw_memories.contains("raw memory"));
assert!(raw_memories.contains(&keep_id));
assert!(raw_memories.contains("cwd: /tmp/workspace"));
assert!(raw_memories.contains("rollout_path: /tmp/rollout-100.jsonl"));
assert!(raw_memories.contains(&format!(
"rollout_summary_file: {canonical_rollout_summary_file}"
)));
@@ -150,6 +152,10 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
.find("cwd: /tmp/workspace")
.map(|offset| thread_pos + offset)
.expect("cwd should exist after thread header");
let rollout_path_pos = raw_memories[thread_pos..]
.find("rollout_path: /tmp/rollout-100.jsonl")
.map(|offset| thread_pos + offset)
.expect("rollout_path should exist after thread header");
let file_pos = raw_memories[thread_pos..]
.find(&format!(
"rollout_summary_file: {canonical_rollout_summary_file}"
@@ -158,7 +164,8 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
.expect("rollout_summary_file should exist after thread header");
assert!(thread_pos < updated_pos);
assert!(updated_pos < cwd_pos);
assert!(cwd_pos < file_pos);
assert!(cwd_pos < rollout_path_pos);
assert!(rollout_path_pos < file_pos);
}
#[tokio::test]
@@ -184,6 +191,7 @@ async fn sync_rollout_summaries_uses_timestamp_hash_and_sanitized_slug_filename(
raw_memory: "raw memory".to_string(),
rollout_summary: "short summary".to_string(),
rollout_slug: Some("Unsafe Slug/With Spaces & Symbols + EXTRA_LONG_12345".to_string()),
rollout_path: PathBuf::from("/tmp/rollout-200.jsonl"),
cwd: PathBuf::from("/tmp/workspace"),
generated_at: Utc.timestamp_opt(201, 0).single().expect("timestamp"),
}];
@@ -239,6 +247,7 @@ async fn sync_rollout_summaries_uses_timestamp_hash_and_sanitized_slug_filename(
.await
.expect("read rollout summary");
assert!(summary.contains(&format!("thread_id: {thread_id}")));
assert!(summary.contains("rollout_path: /tmp/rollout-200.jsonl"));
assert!(
!tokio::fs::try_exists(&stale_unslugged_path)
.await
@@ -283,6 +292,7 @@ task_outcome: success
.to_string(),
rollout_summary: "short summary".to_string(),
rollout_slug: Some("Unsafe Slug/With Spaces & Symbols + EXTRA_LONG_12345".to_string()),
rollout_path: PathBuf::from("/tmp/rollout-200.jsonl"),
cwd: PathBuf::from("/tmp/workspace"),
generated_at: Utc.timestamp_opt(201, 0).single().expect("timestamp"),
}];
@@ -316,6 +326,12 @@ task_outcome: success
let raw_memories = tokio::fs::read_to_string(raw_memories_file(&root))
.await
.expect("read raw memories");
let summary = tokio::fs::read_to_string(
rollout_summaries_dir(&root).join(canonical_rollout_summary_file),
)
.await
.expect("read rollout summary");
assert!(summary.contains("rollout_path: /tmp/rollout-200.jsonl"));
assert!(raw_memories.contains(&format!(
"rollout_summary_file: {canonical_rollout_summary_file}"
)));
@@ -360,6 +376,7 @@ mod phase2 {
raw_memory: "raw memory".to_string(),
rollout_summary: "rollout summary".to_string(),
rollout_slug: None,
rollout_path: PathBuf::from("/tmp/rollout-summary.jsonl"),
cwd: PathBuf::from("/tmp/workspace"),
generated_at: chrono::DateTime::<Utc>::from_timestamp(source_updated_at + 1, 0)
.expect("valid generated_at timestamp"),
@@ -106,7 +106,7 @@ Under `{{ memory_root }}/`:
context.
- source of rollout-level metadata needed for MEMORY.md `### rollout_summary_files`
annotations;
you should be able to find `cwd` and `updated_at` there.
you should be able to find `cwd`, `rollout_path`, and `updated_at` there.
- `MEMORY.md`
- merged memories; produce a lightly clustered version if applicable
- `rollout_summaries/*.md`
@@ -171,7 +171,7 @@ Required task-oriented body shape (strict):
## Task 1: <task description, outcome>
### rollout_summary_files
- <rollout_summaries/file1.md> (cwd=<path>, updated_at=<timestamp>, thread_id=<thread_id>, <optional status/usefulness note>)
- <rollout_summaries/file1.md> (cwd=<path>, rollout_path=<path>, updated_at=<timestamp>, thread_id=<thread_id>, <optional status/usefulness note>)
### keywords
@@ -236,7 +236,8 @@ Schema rules (strict):
- Every `## Task <n>` section must include `### rollout_summary_files`, `### keywords`,
and `### learnings`.
- `### rollout_summary_files` must be task-local (not a block-wide catch-all list).
- Each rollout annotation must include `cwd=<path>` and `updated_at=<timestamp>`.
- Each rollout annotation must include `cwd=<path>`, `rollout_path=<path>`, and
`updated_at=<timestamp>`.
If missing from a rollout summary, recover them from `raw_memories.md`.
- Major learnings should be traceable to rollout summaries listed in the same task section.
- Order rollout references by freshness and practical usefulness.
@@ -25,7 +25,10 @@ Memory layout (general -> specific):
- scripts/ (optional helper scripts)
- examples/ (optional example outputs)
- templates/ (optional templates)
- {{ base_path }}/rollout_summaries/ (per-rollout recaps + evidence snippets)
- {{ base_path }}/rollout_summaries/ (per-rollout recaps + evidence snippets)
- The paths of these entries can be found in {{ base_path }}/MEMORY.md or {{ base_path }}/rollout_summaries/ as `rollout_path`
- These files are append-only `jsonl`: `session_meta.payload.id` identifies the session, `turn_context` marks turn boundaries, `event_msg` is the lightweight status stream, and `response_item` contains actual messages, tool calls, and tool outputs.
- For efficient lookup, prefer matching the filename suffix or `session_meta.payload.id`; avoid broad full-content scans unless needed.
Quick memory pass (when applicable):
@@ -34,7 +37,8 @@ Quick memory pass (when applicable):
3. Only if MEMORY.md directly points to rollout summaries/skills, open the 1-2
most relevant files under {{ base_path }}/rollout_summaries/ or
{{ base_path }}/skills/.
4. If there are no relevant hits, stop memory lookup and continue normally.
4. If above are not clear and you need exact commands, error text, or precise evidence, search over `rollout_path` for more evidence.
5. If there are no relevant hits, stop memory lookup and continue normally.
Quick-pass budget: