use crate::core::agent::harness_kernel::SessionToolCatalogSnapshot;
#[derive(Debug, PartialEq, Eq)]
pub(super) enum AlignmentError {
PlanModeMismatch {
snapshot_plan_mode: bool,
registry_plan_mode: bool,
},
MutatingToolInPlanModePrompt { tool_name: &'static str },
}
pub(super) fn validate_prompt_catalog_alignment(
system_instruction: &str,
tool_snapshot: &SessionToolCatalogSnapshot,
plan_mode: bool,
) -> Result<(), AlignmentError> {
if tool_snapshot.plan_mode != plan_mode {
return Err(AlignmentError::PlanModeMismatch {
snapshot_plan_mode: tool_snapshot.plan_mode,
registry_plan_mode: plan_mode,
});
}
if plan_mode {
const MUTATING_HINTS: &[&str] = &["apply_patch", "unified_file write", "unified_file edit"];
for &hint in MUTATING_HINTS {
if system_instruction.contains(hint) {
return Err(AlignmentError::MutatingToolInPlanModePrompt { tool_name: hint });
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
fn snapshot(plan_mode: bool) -> SessionToolCatalogSnapshot {
SessionToolCatalogSnapshot::new(1, 1, plan_mode, false, Some(Arc::new(Vec::new())), false)
}
#[test]
fn alignment_ok_when_plan_modes_match() {
assert!(
validate_prompt_catalog_alignment("normal prompt", &snapshot(false), false).is_ok()
);
assert!(validate_prompt_catalog_alignment("plan prompt", &snapshot(true), true).is_ok());
}
#[test]
fn plan_mode_mismatch_detected() {
let err = validate_prompt_catalog_alignment("any prompt", &snapshot(false), true)
.expect_err("mismatch should be detected");
assert_eq!(
err,
AlignmentError::PlanModeMismatch {
snapshot_plan_mode: false,
registry_plan_mode: true,
}
);
}
#[test]
fn mutating_tool_in_plan_mode_prompt_detected() {
let err = validate_prompt_catalog_alignment(
"you may call apply_patch to write files",
&snapshot(true),
true,
)
.expect_err("canary should fire");
assert_eq!(
err,
AlignmentError::MutatingToolInPlanModePrompt {
tool_name: "apply_patch"
}
);
}
#[test]
fn no_false_positive_in_normal_mode_with_apply_patch() {
assert!(
validate_prompt_catalog_alignment("you may call apply_patch", &snapshot(false), false)
.is_ok()
);
}
}