mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
fix(tui): refuse a backtrack boundary until every kept prompt is matched
saved_history_boundary accepted the undone turn's prompt while a kept prompt was still unmatched, so a transcript that drifted from the records (a kept turn missing) was cut at the wrong place and lost a kept turn. Now any mismatch before the kept prefix is complete refuses, and the caller preserves the source thread. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
47aa0fdb9f
commit
e941d370af
2 files changed
+34
-3
No files matched your search
@@ -832,9 +832,13 @@ fn saved_history_boundary(
|
||||
};
|
||||
match expected {
|
||||
Some(next) if next == &text => expected = kept.next(),
|
||||
// The first user text that is not part of the kept prefix is where
|
||||
// the undone turn begins — and it has to be that turn's own prompt.
|
||||
_ => return (text == target_prompt).then_some(index),
|
||||
// A kept prompt is still unaccounted for, so this transcript has
|
||||
// drifted from the records; even the undone turn's prompt here
|
||||
// would cut away a turn the backtrack must keep.
|
||||
Some(_) => return None,
|
||||
// The first user text after the whole kept prefix is where the
|
||||
// undone turn begins — and it has to be that turn's own prompt.
|
||||
None => return (text == target_prompt).then_some(index),
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -17483,3 +17483,30 @@ async fn flush_recovery_receipts_drains_every_listed_thread() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_history_boundary_refuses_until_every_kept_prompt_is_seen() {
|
||||
let user = |text: &str| Message {
|
||||
role: Role::User,
|
||||
content: vec![ContentBlock::Text {
|
||||
text: text.to_string(),
|
||||
cache_control: None,
|
||||
}],
|
||||
};
|
||||
let kept = vec!["first".to_string(), "second".to_string()];
|
||||
|
||||
// The kept prompts in order, then the undone turn's prompt: cut there.
|
||||
let messages = vec![user("first"), user("second"), user("undo me")];
|
||||
assert_eq!(saved_history_boundary(&messages, &kept, "undo me"), Some(2));
|
||||
|
||||
// "second" never appears: matching "undo me" early would drop a kept turn.
|
||||
let drifted = vec![user("first"), user("undo me"), user("second")];
|
||||
assert_eq!(saved_history_boundary(&drifted, &kept, "undo me"), None);
|
||||
|
||||
// A repeated prompt stays aligned with the records.
|
||||
let repeated = vec![user("same"), user("same")];
|
||||
assert_eq!(
|
||||
saved_history_boundary(&repeated, &["same".to_string()], "same"),
|
||||
Some(1)
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user