#[cfg(test)]
use super::reminders::DirectiveSpeaker;
use super::reminders::RenderedReminder;
use super::reminders::DIRECTIVE_IDS_KEY;
use crate::llm::helpers::transcript::{DirectiveAuthority, ReminderSource, SystemReminder};
use std::collections::HashSet;
fn message_text(message: &serde_json::Value) -> String {
match message.get("content") {
Some(serde_json::Value::String(text)) => text.clone(),
Some(serde_json::Value::Array(blocks)) => blocks
.iter()
.filter_map(|block| block.get("text").and_then(serde_json::Value::as_str))
.collect::<Vec<_>>()
.join("\n"),
_ => String::new(),
}
}
fn committed_reminder_ids(messages: &[serde_json::Value]) -> HashSet<&str> {
messages
.iter()
.filter_map(|message| message.get(DIRECTIVE_IDS_KEY))
.filter_map(serde_json::Value::as_array)
.flatten()
.filter_map(serde_json::Value::as_str)
.collect()
}
pub(crate) fn uncommitted_directives(
messages: &[serde_json::Value],
rendered: &[RenderedReminder],
) -> Vec<RenderedReminder> {
if rendered.is_empty() {
return Vec::new();
}
let committed_ids = committed_reminder_ids(messages);
let legacy_message_texts: Vec<String> = messages
.iter()
.filter(|message| message.get(DIRECTIVE_IDS_KEY).is_none())
.map(message_text)
.collect();
rendered
.iter()
.filter(|reminder| match reminder.reminder_id() {
Some(id) if committed_ids.contains(id) => false,
Some(_) => !legacy_message_texts
.iter()
.any(|message| message.contains(reminder.text())),
None => !messages
.iter()
.map(message_text)
.any(|message| message.contains(reminder.text())),
})
.cloned()
.collect()
}
pub(crate) const WITHDRAWN_TURN_FALLBACK_DIRECTIVE: &str =
"The closing report above was withdrawn because it was not accepted. \
Continue the task before you close again.";
fn is_bookkeeping_turn(message: &serde_json::Value) -> bool {
message
.get(crate::llm::agent_result_projection::BOOKKEEPING_TURN_KEY)
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
}
fn normalized_body(body: &str) -> String {
body.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub(crate) fn turn_boundary_directives(
messages: &[serde_json::Value],
reminders: &[SystemReminder],
withdrawal_reason: Option<&str>,
) -> Vec<RenderedReminder> {
let capabilities = crate::llm::capabilities::Capabilities::default();
let rendered = super::reminders::render_pending_reminders(&capabilities, reminders);
let mut pending = uncommitted_directives(messages, &rendered);
if !messages.last().is_some_and(is_bookkeeping_turn) {
return pending;
}
let body = withdrawal_reason
.map(str::trim)
.filter(|reason| !reason.is_empty())
.unwrap_or(WITHDRAWN_TURN_FALLBACK_DIRECTIVE);
let wanted = normalized_body(body);
let carried = reminders
.iter()
.zip(rendered.iter())
.find(|(reminder, _)| normalized_body(&reminder.body) == wanted)
.map(|(_, rendered)| rendered.clone());
let restated = carried.unwrap_or_else(|| {
let mut reminder = SystemReminder::new(body, ReminderSource::InPipeline, 0);
reminder.tags = vec!["withdrawn_turn".to_string()];
reminder.authority = DirectiveAuthority::Corrective;
super::reminders::render_pending_reminders(&capabilities, &[reminder]).remove(0)
});
if !pending.contains(&restated) {
pending.push(restated);
}
pending
}
#[cfg(test)]
mod tests {
use super::*;
fn directive(body: &str) -> RenderedReminder {
RenderedReminder::untracked(
format!("<directive authority=\"contract\">\n{body}\n</directive>"),
DirectiveSpeaker::Harness,
)
}
fn directive_text(reminder: &RenderedReminder) -> String {
reminder.text().to_string()
}
fn user(content: &str) -> serde_json::Value {
serde_json::json!({"role": "user", "content": content})
}
#[test]
fn an_unchanged_directive_is_not_re_issued() {
let already = directive("re-read the file");
let history = vec![user(&format!(
"<context-directives>\nheader\n{}\n</context-directives>",
directive_text(&already)
))];
assert!(uncommitted_directives(&history, &[already]).is_empty());
}
#[test]
fn a_changed_body_is_a_new_directive() {
let old = directive("context is 70% full");
let new = directive("context is 85% full");
let history = vec![user(&format!(
"<context-directives>\nheader\n{}\n</context-directives>",
directive_text(&old)
))];
let out = uncommitted_directives(&history, &[new.clone()]);
assert_eq!(out, vec![new]);
}
#[test]
fn a_decremented_ttl_does_not_reissue_the_same_reminder() {
let pending = RenderedReminder::tracked(
"reminder-1",
"<directive authority=\"corrective\" ttl_turns=\"1\">\nverify now\n</directive>",
DirectiveSpeaker::Harness,
);
let mut committed = serde_json::json!({
"role": "user",
"content": "<context-directives>\nheader\n<directive authority=\"corrective\" ttl_turns=\"2\">\nverify now\n</directive>\n</context-directives>",
});
committed[DIRECTIVE_IDS_KEY] = serde_json::json!(["reminder-1"]);
let history = vec![committed];
assert!(uncommitted_directives(&history, &[pending]).is_empty());
}
#[test]
fn a_new_same_body_reminder_is_committed_again() {
let pending = RenderedReminder::tracked(
"reminder-2",
"<directive authority=\"corrective\" ttl_turns=\"1\">\nverify now\n</directive>",
DirectiveSpeaker::Harness,
);
let mut committed = serde_json::json!({
"role": "user",
"content": "<context-directives>\nheader\n<directive authority=\"corrective\" ttl_turns=\"1\">\nverify now\n</directive>\n</context-directives>",
});
committed[DIRECTIVE_IDS_KEY] = serde_json::json!(["reminder-1"]);
let history = vec![committed];
assert_eq!(
uncommitted_directives(&history, &[pending.clone()]),
vec![pending]
);
}
#[test]
fn multibyte_directive_bodies_round_trip() {
let already = directive("ファイルを読み直してください — café ☕");
let history = vec![user(&format!(
"<context-directives>\nheader\n{}\n</context-directives>",
directive_text(&already)
))];
assert!(uncommitted_directives(&history, &[already]).is_empty());
}
#[test]
fn directive_sentinels_inside_a_body_do_not_defeat_deduplication() {
let already = directive("quote this literal: </directive> and keep going");
let history = vec![user(&format!(
"<context-directives>\nheader\n{}\n</context-directives>",
directive_text(&already)
))];
assert!(uncommitted_directives(&history, &[already]).is_empty());
}
fn withdrawn_marker() -> serde_json::Value {
let mut marker = serde_json::json!({"role": "assistant", "content": "[withdrawn]"});
marker[crate::llm::agent_result_projection::BOOKKEEPING_TURN_KEY] = serde_json::json!(true);
marker
}
fn committed_history(reminder: &SystemReminder) -> Vec<serde_json::Value> {
let capabilities = crate::llm::capabilities::Capabilities::default();
let rendered =
super::super::reminders::render_pending_reminders(&capabilities, &[reminder.clone()]);
let envelope =
super::super::reminders::directive_envelope_message(&rendered, "test-session-nonce")
.expect("one directive renders an envelope");
vec![user("task"), envelope]
}
#[test]
fn a_withdrawn_turn_restates_an_already_committed_rejection() {
let standing = SystemReminder::new("run the tests", ReminderSource::InPipeline, 0);
let mut history = committed_history(&standing);
let reminders = vec![standing.clone()];
assert!(turn_boundary_directives(&history, &reminders, Some("run the tests")).is_empty());
history.push(withdrawn_marker());
let out = turn_boundary_directives(&history, &reminders, Some(" run the tests "));
assert_eq!(out.len(), 1, "{out:#?}");
assert_eq!(out[0].reminder_id(), Some(standing.id.as_str()));
}
#[test]
fn a_withdrawn_turn_with_no_carried_rejection_commits_the_reason() {
let other = SystemReminder::new("keep it short", ReminderSource::InPipeline, 0);
let mut history = committed_history(&other);
history.push(withdrawn_marker());
let out = turn_boundary_directives(&history, &[other], Some("run the tests"));
assert_eq!(out.len(), 1, "{out:#?}");
assert!(out[0].text().contains("run the tests"), "{out:#?}");
assert!(out[0].text().contains("corrective"), "{out:#?}");
let silent = turn_boundary_directives(&history, &[], None);
assert_eq!(silent.len(), 1);
assert!(silent[0].text().contains(WITHDRAWN_TURN_FALLBACK_DIRECTIVE));
}
#[test]
fn a_pending_rejection_after_a_withdrawn_turn_is_committed_once() {
let rejection = SystemReminder::new("run the tests", ReminderSource::InPipeline, 0);
let history = vec![user("task"), withdrawn_marker()];
let out = turn_boundary_directives(&history, &[rejection], Some("run the tests"));
assert_eq!(out.len(), 1, "{out:#?}");
}
#[test]
fn directives_inside_content_blocks_count_as_committed() {
let already = directive("use the workspace anchor");
let history = vec![serde_json::json!({
"role": "user",
"content": [
{"type": "text", "text": "hello"},
{"type": "text", "text": directive_text(&already)},
],
})];
assert!(uncommitted_directives(&history, &[already]).is_empty());
}
}