use super::*;
use super::test_support::{
active_prompt_card, assistant_call, recording_summarizer, run, sys, tool_result, user, EST,
};
use serde_json::json;
use std::sync::{Arc, Mutex};
#[tokio::test]
async fn prior_turn_task_survives_compaction_when_active_prompt_is_ceremony() {
let real_task = "STEER-TASK-7c41: extract one cohesive #[cfg(test)] module \
from newt-core/src/agentic/mod.rs into a sibling file by pure code \
motion, keep the build green, then open exactly one PR.";
let ceremony = "1: proceed";
let mut msgs = vec![
sys("you are newt"),
user(real_task),
serde_json::json!({
"role": "assistant",
"content": "I need these decisions locked before I can execute. \
Reply using an explicit ordinal: 1. Pick the single largest…"
}),
user(ceremony),
];
for i in 0..12 {
msgs.push(assistant_call(
"read_file",
json!({"path": "newt-core/src/agentic/mod.rs", "offset": i * 500}),
));
msgs.push(tool_result(&"m".repeat(4_000)));
}
let store = crate::agentic::prompt_read::SessionPromptStore::default();
let task_turn = store
.begin_prompt(
"conv-steer",
crate::prompt::NewPrompt::operator(real_task.as_bytes(), real_task.as_bytes()),
)
.expect("task receipt");
let ceremony_turn = store
.begin_prompt(
"conv-steer",
crate::prompt::NewPrompt::operator_continuation(
ceremony.as_bytes(),
ceremony.as_bytes(),
task_turn.submitted_prompt().id(),
),
)
.expect("ceremony receipt");
let active_task =
crate::agentic::prompt_read::PromptReadContext::new(Some(&ceremony_turn), ceremony, None)
.active_text();
let protected = protect_active_prompt_for_compression(&msgs, active_task);
let before = estimate_tokens(&protected, EST);
let prompts = Arc::new(Mutex::new(Vec::new()));
let s = recording_summarizer(prompts.clone(), "## Summary\nreads happened");
let mut state = CompressState::new();
let out = compress(
CompressRequest {
rewrites_history: true,
messages: &protected,
budget: before / 4,
max_messages: None,
replay_protected_tail_len: 0,
task: active_task,
hard_budget: true,
authoritative: true,
focus: None,
est: EST,
summary_input_cap_floor_chars: 8_192,
compaction_store: None,
compaction_stage: None,
},
Some(&*s),
&mut state,
)
.await;
assert!(out.fired, "the oversized middle must trigger compaction");
let visible: String = out
.messages
.iter()
.filter_map(|m| m["content"].as_str())
.collect::<Vec<_>>()
.join("\n");
assert!(
visible.contains("STEER-TASK-7c41"),
"the REAL task from the prior turn must survive compaction verbatim \
even when the current turn's active prompt is decision ceremony \
(\"{ceremony}\") — otherwise the agent keeps working with no goal. \
Post-compaction visible content:\n{visible}"
);
}
#[tokio::test]
async fn summarized_file_reads_get_a_reread_breadcrumb() {
let sig = "pub fn connect(&self, url: &str, timeout: Duration) -> Result<Session, ConnErr>";
let api_body = format!(
"pub struct ApiClient;\nimpl ApiClient {{\n {sig} {{ todo!() }}\n}}\n{}",
"// detail line\n".repeat(200)
);
let mut msgs = vec![
sys("you are newt, a coding agent"),
active_prompt_card(),
user("ACTIVE TASK: implement reconnect() on ApiClient using its connect() method"),
assistant_call("read_file", json!({ "path": "src/api.rs" })),
tool_result(&api_body), ];
for i in 0..8 {
msgs.push(assistant_call(
"read_file",
json!({ "path": format!("src/other_{i}.rs") }),
));
msgs.push(tool_result(&format!(
"// other file {i}\n{}",
"filler line\n".repeat(150)
)));
}
let before = estimate_tokens(&msgs, EST);
let prompts = Arc::new(Mutex::new(Vec::new()));
let s = recording_summarizer(
prompts.clone(),
"## Active Task\nImplement reconnect(). The agent earlier read src/api.rs \
(defines ApiClient) and several other files.",
);
let mut state = CompressState::new();
let out = run(&msgs, before / 2, None, Some(&*s), &mut state).await;
let assembled: String = out
.messages
.iter()
.filter_map(|m| m["content"].as_str())
.collect::<Vec<_>>()
.join("\n");
eprintln!(
"#319: fired={} action={:?}\n{}",
out.fired,
out.action,
&assembled[..assembled.len().min(1200)]
);
assert!(out.fired && out.action == CompressAction::Summarized);
assert!(
assembled.contains("src/api.rs"),
"the dropped file must be named so the model knows to re-read it"
);
assert!(
assembled.contains("RE-READ") && assembled.contains("do NOT recall"),
"the breadcrumb must carry the re-read / don't-recall directive"
);
}
#[tokio::test]
async fn most_recent_target_read_is_pinned_and_survives_compaction() {
let target = "newt-core/src/agentic/mod.rs";
let marker = "fn WORKING_SET_MARKER_edit_me()";
let body = format!("{marker} {{\n{}}}\n", " // body line\n".repeat(120));
let mut msgs = vec![
sys("you are newt"),
active_prompt_card(),
user("ACTIVE TASK: reduce mod.rs below 5000 lines by pure code motion"),
];
for i in 0..6 {
msgs.push(assistant_call(
"read_file",
json!({ "path": format!("src/other_{i}.rs") }),
));
msgs.push(tool_result(&format!(
"// other file {i}\n{}",
"filler line\n".repeat(120)
)));
}
msgs.push(assistant_call("read_file", json!({ "path": target })));
msgs.push(tool_result(&body));
for i in 0..6 {
msgs.push(assistant_call(
"run_command",
json!({ "cmd": format!("git status {i}") }),
));
msgs.push(tool_result(&format!(
"bookkeeping {i}\n{}",
"status line\n".repeat(120)
)));
}
let before = estimate_tokens(&msgs, EST);
let prompts = Arc::new(Mutex::new(Vec::new()));
let s = recording_summarizer(
prompts.clone(),
"## Active Task\nReduce mod.rs by code motion. The agent read several files.",
);
let mut state = CompressState::new();
let out = run(&msgs, before / 3, None, Some(&*s), &mut state).await;
let assembled: String = out
.messages
.iter()
.filter_map(|m| m["content"].as_str())
.collect::<Vec<_>>()
.join("\n");
assert!(
out.fired && out.action == CompressAction::Summarized,
"summary must fire for this to test protection (action={:?})",
out.action
);
assert!(
assembled.contains(marker),
"the most-recent target read must be PINNED and survive compaction so \
the model can edit from it instead of looping on re-reads; assembled:\n{}",
&assembled[..assembled.len().min(1600)]
);
}
#[tokio::test]
async fn working_set_pin_tracks_the_latest_read_not_the_first() {
let first = "src/first.rs";
let second = "src/second.rs";
let first_marker = "fn FIRST_FILE_MARKER()";
let second_marker = "fn SECOND_FILE_MARKER()";
let mut msgs = vec![
sys("you are newt"),
active_prompt_card(),
user("ACTIVE TASK: refactor two files"),
];
msgs.push(assistant_call("read_file", json!({ "path": first })));
msgs.push(tool_result(&format!(
"{first_marker} {{\n{}}}\n",
" // a\n".repeat(60)
)));
for i in 0..4 {
msgs.push(assistant_call(
"run_command",
json!({ "cmd": format!("git a{i}") }),
));
msgs.push(tool_result(&format!("bk {i}\n{}", "x\n".repeat(120))));
}
msgs.push(assistant_call("read_file", json!({ "path": second })));
msgs.push(tool_result(&format!(
"{second_marker} {{\n{}}}\n",
" // b\n".repeat(60)
)));
for i in 0..4 {
msgs.push(assistant_call(
"run_command",
json!({ "cmd": format!("git b{i}") }),
));
msgs.push(tool_result(&format!("bk2 {i}\n{}", "y\n".repeat(120))));
}
let before = estimate_tokens(&msgs, EST);
let prompts = Arc::new(Mutex::new(Vec::new()));
let s = recording_summarizer(prompts.clone(), "## Active Task\nrefactor two files.");
let mut state = CompressState::new();
let out = run(&msgs, before / 3, None, Some(&*s), &mut state).await;
let assembled: String = out
.messages
.iter()
.filter_map(|m| m["content"].as_str())
.collect::<Vec<_>>()
.join("\n");
assert!(out.fired && out.action == CompressAction::Summarized);
assert!(
assembled.contains(second_marker),
"the latest read (second.rs) must be the pinned working set"
);
assert!(
!assembled.contains(&format!("- {second}")),
"the pinned file must be excluded from the re-read breadcrumb"
);
assert!(
!assembled.contains(first_marker),
"the superseded file's body must not linger as a second pin"
);
}
#[tokio::test]
async fn knowledge_base_stable_base_survives_compression() {
let kb = "## Authoritative import surface\n\
from newt_agent._newt_agent.core import Router # real path, not a guess";
let mut msgs = vec![sys(kb), user("task")];
for i in 0..24 {
msgs.push(user(&format!("middle note {i} {}", "m".repeat(200))));
}
msgs.push(user("recent tail"));
let mut state = CompressState::new();
let out = run(&msgs, 300, None, None, &mut state).await;
assert!(out.fired, "a large conversation should compress");
assert!(
out.messages.iter().any(|m| m["role"] == "system"
&& m["content"]
.as_str()
.is_some_and(|c| c.contains("from newt_agent._newt_agent.core import Router"))),
"the knowledge_base import surface must survive compression VERBATIM \
(the protected head — the stable base E relies on)"
);
}