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:
parent
6acede5a28
commit
52aa49db1b
6 changed files with 39 additions and 7 deletions
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use super::ThreadMetadata;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Stage1Output {
|
||||
pub thread_id: ThreadId,
|
||||
pub rollout_path: PathBuf,
|
||||
pub source_updated_at: DateTime<Utc>,
|
||||
pub raw_memory: String,
|
||||
pub rollout_summary: String,
|
||||
|
|
@ -23,6 +24,7 @@ pub struct Stage1Output {
|
|||
#[derive(Debug)]
|
||||
pub(crate) struct Stage1OutputRow {
|
||||
thread_id: String,
|
||||
rollout_path: String,
|
||||
source_updated_at: i64,
|
||||
raw_memory: String,
|
||||
rollout_summary: String,
|
||||
|
|
@ -35,6 +37,7 @@ impl Stage1OutputRow {
|
|||
pub(crate) fn try_from_row(row: &SqliteRow) -> Result<Self> {
|
||||
Ok(Self {
|
||||
thread_id: row.try_get("thread_id")?,
|
||||
rollout_path: row.try_get("rollout_path")?,
|
||||
source_updated_at: row.try_get("source_updated_at")?,
|
||||
raw_memory: row.try_get("raw_memory")?,
|
||||
rollout_summary: row.try_get("rollout_summary")?,
|
||||
|
|
@ -51,6 +54,7 @@ impl TryFrom<Stage1OutputRow> for Stage1Output {
|
|||
fn try_from(row: Stage1OutputRow) -> std::result::Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
thread_id: ThreadId::try_from(row.thread_id)?,
|
||||
rollout_path: PathBuf::from(row.rollout_path),
|
||||
source_updated_at: epoch_seconds_to_datetime(row.source_updated_at)?,
|
||||
raw_memory: row.raw_memory,
|
||||
rollout_summary: row.rollout_summary,
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ LEFT JOIN jobs
|
|||
///
|
||||
/// Query behavior:
|
||||
/// - filters out rows where both `raw_memory` and `rollout_summary` are blank
|
||||
/// - joins `threads` to include thread `cwd`
|
||||
/// - joins `threads` to include thread `cwd` and `rollout_path`
|
||||
/// - orders by `source_updated_at DESC, thread_id DESC`
|
||||
/// - applies `LIMIT n`
|
||||
pub async fn list_stage1_outputs_for_global(
|
||||
|
|
@ -233,6 +233,7 @@ LEFT JOIN jobs
|
|||
r#"
|
||||
SELECT
|
||||
so.thread_id,
|
||||
COALESCE(t.rollout_path, '') AS rollout_path,
|
||||
so.source_updated_at,
|
||||
so.raw_memory,
|
||||
so.rollout_summary,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue