use std::collections::HashMap;
use super::decomposition::DelegationProvenance;
use super::intent_registry::Intent;
use super::guard_impls::behavioral::{
DeclaredActionGuard, InternalJargonGuard, PerspectiveGuard, SubagentClaimGuard,
TaskDeferralGuard,
};
use super::guard_impls::output_quality::{
EmptyResponseGuard, LowValueParrotingGuard, NonRepetitionGuard, OutputContractGuard,
UserEchoGuard,
};
use super::guard_impls::protocol::InternalProtocolGuard;
use super::guard_impls::truthfulness::{
CurrentEventsTruthGuard, ExecutionTruthGuard, LiteraryQuoteRetryGuard, ModelIdentityTruthGuard,
PersonalityIntegrityGuard,
};
pub(super) use super::guard_impls::protocol::contains_internal_protocol_marker;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum GuardId {
EmptyResponse,
SubagentClaim,
ExecutionTruth,
TaskDeferral,
OutputContract,
ModelIdentityTruth,
CurrentEventsTruth,
LiteraryQuoteRetry,
PersonalityIntegrity,
InternalJargon,
NonRepetition,
LowValueParroting,
InternalProtocol,
Perspective,
UserEcho,
DeclaredAction,
}
pub(super) enum GuardVerdict {
Pass,
Rewritten(String),
RetryRequested { reason: String },
}
pub(super) struct GuardContext<'a> {
pub user_prompt: &'a str,
pub intents: &'a [Intent],
pub tool_results: &'a [(String, String)],
pub agent_name: &'a str,
pub resolved_model: &'a str,
pub delegation_provenance: &'a DelegationProvenance,
pub previous_assistant: Option<&'a str>,
pub prior_assistant_messages: &'a [String],
pub semantic_guard_scores:
HashMap<String, (f64, roboticus_llm::semantic_classifier::TrustLevel)>,
pub subagent_names: Vec<String>,
}
impl<'a> GuardContext<'a> {
pub(super) fn has_intent(&self, intent: Intent) -> bool {
self.intents.contains(&intent)
}
pub(super) fn for_streaming(
user_prompt: &'a str,
intents: &'a [Intent],
agent_name: &'a str,
resolved_model: &'a str,
delegation_provenance: &'a super::decomposition::DelegationProvenance,
semantic_guard_scores: HashMap<
String,
(f64, roboticus_llm::semantic_classifier::TrustLevel),
>,
subagent_names: Vec<String>,
) -> Self {
Self {
user_prompt,
intents,
tool_results: &[],
agent_name,
resolved_model,
delegation_provenance,
previous_assistant: None,
prior_assistant_messages: &[],
semantic_guard_scores,
subagent_names,
}
}
}
pub(super) fn guard_context_is_task_like(ctx: &GuardContext<'_>) -> bool {
ctx.has_intent(Intent::Execution)
|| ctx.has_intent(Intent::TaskManagement)
|| ctx.has_intent(Intent::Delegation)
|| ctx.has_intent(Intent::Cron)
|| ctx.has_intent(Intent::FileDistribution)
|| ctx.has_intent(Intent::FolderScan)
|| ctx.has_intent(Intent::WalletAddressScan)
|| ctx.has_intent(Intent::ImageCountScan)
|| ctx.has_intent(Intent::MarkdownCountScan)
|| ctx.has_intent(Intent::ObsidianInsights)
|| ctx.has_intent(Intent::EmailTriage)
|| ctx.has_intent(Intent::CurrentEvents)
}
pub(super) fn first_absolute_path(prompt: &str) -> Option<String> {
prompt.split_whitespace().find_map(|token| {
let cleaned = token
.trim_matches(|c: char| matches!(c, '"' | '\'' | ',' | '.' | ';' | ':' | ')' | '('));
if cleaned.starts_with('/') && cleaned.len() > 1 {
Some(cleaned.to_string())
} else {
None
}
})
}
pub(super) trait Guard: Send + Sync {
fn id(&self) -> GuardId;
fn is_relevant(&self, ctx: &GuardContext) -> bool;
fn evaluate(&self, content: &str, ctx: &GuardContext) -> GuardVerdict;
}
pub(super) struct RetrySignal {
pub guard_id: GuardId,
pub reason: String,
#[allow(dead_code)]
pub resume_index: usize,
}
pub(super) struct GuardChainResult {
pub content: String,
pub retry: Option<RetrySignal>,
}
pub(super) struct GuardChain {
guards: Vec<Box<dyn Guard>>,
}
impl GuardChain {
pub fn empty() -> Self {
Self { guards: vec![] }
}
pub fn apply(&self, content: String, ctx: &GuardContext) -> GuardChainResult {
self.apply_from(content, ctx, 0)
}
#[cfg(test)]
pub fn is_empty(&self) -> bool {
self.guards.is_empty()
}
#[cfg(test)]
pub fn len(&self) -> usize {
self.guards.len()
}
pub fn apply_from(
&self,
mut content: String,
ctx: &GuardContext,
from: usize,
) -> GuardChainResult {
for (i, guard) in self.guards.iter().enumerate().skip(from) {
if !guard.is_relevant(ctx) {
continue;
}
match guard.evaluate(&content, ctx) {
GuardVerdict::Pass => {}
GuardVerdict::Rewritten(new) => content = new,
GuardVerdict::RetryRequested { reason } => {
return GuardChainResult {
content,
retry: Some(RetrySignal {
guard_id: guard.id(),
reason,
resume_index: i + 1,
}),
};
}
}
}
GuardChainResult {
content,
retry: None,
}
}
}
pub(super) mod guard_sets {
use super::*;
pub fn full() -> GuardChain {
GuardChain {
guards: vec![
Box::new(EmptyResponseGuard),
Box::new(SubagentClaimGuard),
Box::new(ExecutionTruthGuard),
Box::new(TaskDeferralGuard),
Box::new(OutputContractGuard),
Box::new(ModelIdentityTruthGuard),
Box::new(CurrentEventsTruthGuard),
Box::new(LiteraryQuoteRetryGuard),
Box::new(PersonalityIntegrityGuard),
Box::new(InternalJargonGuard),
Box::new(NonRepetitionGuard),
Box::new(LowValueParrotingGuard),
Box::new(PerspectiveGuard),
Box::new(DeclaredActionGuard),
Box::new(UserEchoGuard),
Box::new(InternalProtocolGuard),
],
}
}
pub fn cached() -> GuardChain {
GuardChain {
guards: vec![
Box::new(EmptyResponseGuard),
Box::new(SubagentClaimGuard), Box::new(ExecutionTruthGuard),
Box::new(TaskDeferralGuard),
Box::new(OutputContractGuard),
Box::new(ModelIdentityTruthGuard),
Box::new(CurrentEventsTruthGuard),
Box::new(LiteraryQuoteRetryGuard), Box::new(PersonalityIntegrityGuard),
Box::new(InternalJargonGuard),
Box::new(InternalProtocolGuard),
Box::new(NonRepetitionGuard),
Box::new(LowValueParrotingGuard),
],
}
}
pub fn streaming() -> GuardChain {
GuardChain {
guards: vec![
Box::new(SubagentClaimGuard),
Box::new(CurrentEventsTruthGuard),
Box::new(PersonalityIntegrityGuard),
Box::new(InternalJargonGuard),
Box::new(NonRepetitionGuard),
Box::new(InternalProtocolGuard),
],
}
}
}
pub(super) async fn precompute_guard_scores(
classifier: &roboticus_llm::semantic_classifier::SemanticClassifier,
response_text: &str,
) -> HashMap<String, (f64, roboticus_llm::semantic_classifier::TrustLevel)> {
use roboticus_llm::intent_exemplars::GUARD_EXEMPLARS;
match classifier
.classify(response_text, GUARD_EXEMPLARS, 0.0, None)
.await
{
Ok(results) => results
.into_iter()
.map(|r| (r.category, (r.score, r.trust)))
.collect(),
Err(e) => {
tracing::debug!(error = %e, "guard semantic pre-computation failed, using keyword fallback");
HashMap::new()
}
}
}
pub(super) fn requested_exact_bullet_count(prompt: &str) -> Option<usize> {
let lower = prompt.to_ascii_lowercase();
if !(lower.contains("exactly") && lower.contains("bullet")) {
return None;
}
let word_matches = [
("one", 1usize),
("two", 2),
("three", 3),
("four", 4),
("five", 5),
("six", 6),
("seven", 7),
("eight", 8),
("nine", 9),
("ten", 10),
]
.into_iter()
.filter_map(|(word, value)| lower.contains(word).then_some(value))
.max();
let numeric_matches = lower
.split_whitespace()
.filter_map(|token| token.parse::<usize>().ok())
.max();
word_matches.into_iter().chain(numeric_matches).max()
}
pub(super) fn extract_bullet_lines(content: &str) -> Vec<String> {
content
.lines()
.filter_map(|line| {
let trimmed = line.trim();
let is_bullet = trimmed.starts_with("- ")
|| trimmed.starts_with("* ")
|| trimmed.starts_with("• ")
|| trimmed.starts_with("-\t")
|| trimmed.starts_with("*\t")
|| trimmed.starts_with("•\t");
is_bullet.then(|| trimmed.to_string())
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn default_provenance() -> DelegationProvenance {
DelegationProvenance::default()
}
fn ctx<'a>(
prompt: &'a str,
intents: &'a [Intent],
tool_results: &'a [(String, String)],
provenance: &'a DelegationProvenance,
) -> GuardContext<'a> {
GuardContext {
user_prompt: prompt,
intents,
tool_results,
agent_name: "TestAgent",
resolved_model: "test-model",
delegation_provenance: provenance,
previous_assistant: None,
prior_assistant_messages: &[],
semantic_guard_scores: HashMap::new(),
subagent_names: vec![],
}
}
#[test]
fn subagent_claim_passes_when_provenance_valid() {
let prov = DelegationProvenance {
subagent_task_started: true,
subagent_task_completed: true,
subagent_result_attached: true,
};
let guard = SubagentClaimGuard;
let ctx = ctx("test", &[], &[], &prov);
let verdict = guard.evaluate("subagent-generated sitrep: all clear", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn subagent_claim_blocks_without_provenance() {
let prov = default_provenance();
let guard = SubagentClaimGuard;
let ctx = ctx("test", &[], &[], &prov);
let verdict = guard.evaluate("subagent-generated sitrep: all clear", &ctx);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn subagent_claim_passes_when_no_claim() {
let prov = default_provenance();
let guard = SubagentClaimGuard;
let ctx = ctx("test", &[], &[], &prov);
let verdict = guard.evaluate("Here is a normal response.", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn execution_truth_blocks_false_delegation_claim() {
let prov = default_provenance();
let intents = [Intent::Delegation];
let guard = ExecutionTruthGuard;
let mut gctx = ctx("delegate to a subagent", &intents, &[], &prov);
gctx.semantic_guard_scores.insert(
"FALSE_COMPLETION".to_string(),
(0.85, roboticus_llm::semantic_classifier::TrustLevel::Neural),
);
let verdict = guard.evaluate("I delegated the task successfully.", &gctx);
assert!(matches!(verdict, GuardVerdict::Rewritten(_)));
}
#[test]
fn execution_truth_passes_recommendation_without_false_completion() {
let prov = default_provenance();
let intents = [Intent::Delegation];
let guard = ExecutionTruthGuard;
let ctx = ctx(
"what should my subagents be working on",
&intents,
&[],
&prov,
);
let verdict = guard.evaluate(
"I'd re-assign the Revenue Strategist to focus on tax optimization.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn execution_truth_passes_with_real_delegation_tool() {
let prov = default_provenance();
let intents = [Intent::Delegation];
let tools = vec![("delegate-subagent".into(), "success".into())];
let guard = ExecutionTruthGuard;
let ctx = ctx("delegate to a subagent", &intents, &tools, &prov);
let verdict = guard.evaluate("I delegated the task successfully.", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn execution_truth_blocks_unexecuted_claim_no_tools() {
let prov = default_provenance();
let intents = [Intent::Execution];
let guard = ExecutionTruthGuard;
let ctx = ctx("run the scanner", &intents, &[], &prov);
let verdict = guard.evaluate("tool successfully executed and returned results", &ctx);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn execution_truth_fix_blocks_capability_denial_despite_tools() {
let prov = default_provenance();
let intents = [Intent::FolderScan];
let tools = vec![("list-files".into(), "file1.txt\nfile2.txt".into())];
let guard = ExecutionTruthGuard;
let ctx = ctx("scan my ~/Downloads folder", &intents, &tools, &prov);
let verdict = guard.evaluate(
"I cannot access your local files or folders on your device.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::Rewritten(_)));
}
#[test]
fn execution_truth_passes_normal_response_with_tools() {
let prov = default_provenance();
let intents = [Intent::Execution];
let tools = vec![("shell".into(), "hello world".into())];
let guard = ExecutionTruthGuard;
let ctx = ctx("run echo hello", &intents, &tools, &prov);
let verdict = guard.evaluate("The command returned: hello world", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn model_identity_emits_canonical_response() {
let prov = default_provenance();
let intents = [Intent::ModelIdentity];
let guard = ModelIdentityTruthGuard;
let ctx = ctx("/status", &intents, &[], &prov);
let verdict = guard.evaluate("I am a helpful assistant.", &ctx);
match verdict {
GuardVerdict::Rewritten(msg) => {
assert!(msg.contains("TestAgent"));
assert!(msg.contains("test-model"));
}
_ => panic!("expected Rewritten"),
}
}
#[test]
fn current_events_blocks_stale_disclaimer() {
let prov = default_provenance();
let intents = [Intent::CurrentEvents];
let guard = CurrentEventsTruthGuard;
let ctx = ctx("geopolitical sitrep", &intents, &[], &prov);
let verdict = guard.evaluate(
"As of my last update in 2023, I cannot provide real-time updates.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::Rewritten(_)));
}
#[test]
fn current_events_passes_live_content() {
let prov = default_provenance();
let intents = [Intent::CurrentEvents];
let guard = CurrentEventsTruthGuard;
let ctx = ctx("geopolitical sitrep", &intents, &[], &prov);
let verdict = guard.evaluate("Here is the latest sitrep from live data.", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn literary_quote_requests_retry_on_overbroad_refusal() {
let prov = default_provenance();
let intents = [Intent::LiteraryQuoteContext];
let guard = LiteraryQuoteRetryGuard;
let ctx = ctx("dune quote for iran conflict", &intents, &[], &prov);
let verdict = guard.evaluate(
"I cannot provide quotes related to ongoing conflicts due to sensitive geopolitical situations.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn personality_strips_foreign_identity() {
let prov = default_provenance();
let guard = PersonalityIntegrityGuard;
let ctx = ctx("hello", &[], &[], &prov);
let verdict = guard.evaluate(
"As an AI developed by Microsoft, I can help you. Here is your answer.",
&ctx,
);
match verdict {
GuardVerdict::Rewritten(msg) => {
assert!(!msg.to_ascii_lowercase().contains("microsoft"));
assert!(msg.contains("answer"));
}
_ => panic!("expected Rewritten"),
}
}
#[test]
fn jargon_retries_on_high_narration_score() {
let prov = default_provenance();
let guard = InternalJargonGuard;
let mut scores = HashMap::new();
scores.insert(
"NARRATED_DELEGATION".to_string(),
(0.9, roboticus_llm::semantic_classifier::TrustLevel::Neural),
);
let mut gctx = ctx("hello", &[], &[], &prov);
gctx.semantic_guard_scores = scores;
let verdict = guard.evaluate(
"I routed your request through the delegation gate and it decided centralized.",
&gctx,
);
assert!(
matches!(verdict, GuardVerdict::RetryRequested { .. }),
"expected RetryRequested when NARRATED_DELEGATION score > 0.8"
);
}
#[test]
fn jargon_allows_subagent_mention_when_user_asked() {
let prov = default_provenance();
let guard = InternalJargonGuard;
let mut gctx = ctx("have sentinel run a security scan", &[], &[], &prov);
gctx.subagent_names = vec!["sentinel".to_string()];
let verdict = guard.evaluate(
"Sentinel has completed the security scan. Here are the results.",
&gctx,
);
assert!(
matches!(verdict, GuardVerdict::Pass),
"mentioning a subagent the user asked about should not trigger a leak"
);
}
#[test]
fn jargon_flags_subagent_mention_when_user_didnt_ask() {
let prov = default_provenance();
let guard = InternalJargonGuard;
let mut gctx = ctx("what is the weather?", &[], &[], &prov);
gctx.subagent_names = vec!["sentinel".to_string()];
let verdict = guard.evaluate("I asked sentinel to check the weather for you.", &gctx);
assert!(
matches!(verdict, GuardVerdict::RetryRequested { .. }),
"mentioning a subagent the user didn't ask about should trigger a leak"
);
}
#[test]
fn non_repetition_detects_exact_repeat() {
let prov = default_provenance();
let mut gctx = ctx("give me a status update", &[], &[], &prov);
let long = "a]".repeat(50); gctx.previous_assistant = Some(&long);
let guard = NonRepetitionGuard;
let verdict = guard.evaluate(&long, &gctx);
assert!(matches!(verdict, GuardVerdict::Rewritten(_)));
}
#[test]
fn low_value_detects_placeholder() {
let prov = default_provenance();
let guard = LowValueParrotingGuard;
let ctx = ctx("tell me about X", &[], &[], &prov);
let verdict = guard.evaluate("Ready", &ctx);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn low_value_passes_acknowledgement_intent() {
let prov = default_provenance();
let intents = [Intent::Acknowledgement];
let guard = LowValueParrotingGuard;
let ctx = ctx(
"acknowledge in one sentence then wait",
&intents,
&[],
&prov,
);
let verdict = guard.evaluate("Ready", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn protocol_strips_tool_call_json() {
let prov = default_provenance();
let guard = InternalProtocolGuard;
let ctx = ctx("do something", &[], &[], &prov);
let verdict = guard.evaluate(
"{\"tool_call\": {\"name\": \"shell\"}}\nActual response here.",
&ctx,
);
match verdict {
GuardVerdict::Rewritten(msg) => {
assert!(!msg.contains("tool_call"));
assert!(msg.contains("Actual response"));
}
_ => panic!("expected Rewritten"),
}
}
#[test]
fn protocol_empty_strip_requests_retry_for_task_turns() {
let prov = default_provenance();
let guard = InternalProtocolGuard;
let intents = [Intent::Execution];
let ctx = ctx("do something", &intents, &[], &prov);
let verdict = guard.evaluate("{\"tool_call\": {\"name\": \"shell\"}}", &ctx);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn output_contract_requests_retry_for_extra_framing() {
let prov = default_provenance();
let guard = OutputContractGuard;
let intents = [Intent::Execution];
let ctx = ctx(
"Inspect what you need, then summarize anything notable in exactly three bullet points.",
&intents,
&[],
&prov,
);
let verdict = guard.evaluate("## Summary\n- one\n- two\n- three", &ctx);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn output_contract_passes_exact_bullets_only() {
let prov = default_provenance();
let guard = OutputContractGuard;
let intents = [Intent::Execution];
let ctx = ctx("Return exactly three bullet points.", &intents, &[], &prov);
let verdict = guard.evaluate("- one\n- two\n- three", &ctx);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn requested_exact_bullet_count_prefers_bullet_contract_over_other_counts() {
assert_eq!(
requested_exact_bullet_count(
"Return exactly three bullet points: two revenue ideas and one SaaS idea."
),
Some(3)
);
}
#[test]
fn task_deferral_requests_retry_for_narrated_next_step_after_introspection() {
let prov = default_provenance();
let intents = [Intent::Execution];
let tools = [(
"list-subagent-roster".to_string(),
"no subagents configured".to_string(),
)];
let guard = TaskDeferralGuard;
let mut gctx = ctx("handle this task", &intents, &tools, &prov);
gctx.semantic_guard_scores.insert(
"TASK_DEFERRAL".to_string(),
(0.85, roboticus_llm::semantic_classifier::TrustLevel::Neural),
);
let verdict = guard.evaluate(
"No subagents are currently configured. Let me compose the specialists needed for this task.",
&gctx,
);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn task_deferral_fires_on_high_semantic_score() {
let prov = default_provenance();
let intents = [Intent::Execution];
let tools = [(
"get_subagent_status".to_string(),
"{\"subagent_count\":0,\"subagents\":[]}".to_string(),
)];
let guard = TaskDeferralGuard;
let mut gctx = ctx("handle this task", &intents, &tools, &prov);
gctx.semantic_guard_scores.insert(
"TASK_DEFERRAL".to_string(),
(0.85, roboticus_llm::semantic_classifier::TrustLevel::Neural),
);
let verdict = guard.evaluate(
"Perfect! I can see there are no active subagents currently available. Now I'll compose the necessary specialists for this task.",
&gctx,
);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn task_deferral_passes_on_low_semantic_score() {
let prov = default_provenance();
let intents = [Intent::Execution];
let tools = [(
"get_subagent_status".to_string(),
"{\"subagent_count\":0,\"subagents\":[]}".to_string(),
)];
let guard = TaskDeferralGuard;
let gctx = ctx("handle this task", &intents, &tools, &prov);
let verdict = guard.evaluate(
"Here are the results from the subagent status check. No agents are configured.",
&gctx,
);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn task_deferral_rewrites_filesystem_boundary_blocker_from_runtime_context() {
let prov = default_provenance();
let intents = [Intent::Execution];
let tools = [(
"get_runtime_context".to_string(),
"{\"workspace\":\"/Users/jmachen/.roboticus/workspace\"}".to_string(),
)];
let guard = TaskDeferralGuard;
let mut gctx = ctx(
"This is a task. Inspect what you need, then read /root/forbidden.txt and return exactly one bullet point with the result.",
&intents,
&tools,
&prov,
);
gctx.semantic_guard_scores.insert(
"TASK_DEFERRAL".to_string(),
(0.9, roboticus_llm::semantic_classifier::TrustLevel::Neural),
);
let verdict = guard.evaluate(
"I'll inspect the runtime context first to understand my sandbox boundaries, then read the forbidden.txt file.",
&gctx,
);
match verdict {
GuardVerdict::Rewritten(text) => {
assert!(text.starts_with("- Blocked: /root/forbidden.txt"));
}
_ => panic!("expected filesystem blocker rewrite"),
}
}
#[test]
fn full_chain_applies_all_relevant_guards() {
let chain = guard_sets::full();
let ids: Vec<GuardId> = chain.guards.iter().map(|g| g.id()).collect();
assert_eq!(chain.len(), 16);
assert!(
ids.contains(&GuardId::OutputContract),
"full must include OutputContract"
);
assert!(
ids.contains(&GuardId::TaskDeferral),
"full must include TaskDeferral"
);
assert!(
ids.contains(&GuardId::InternalProtocol),
"full must include InternalProtocol"
);
}
#[test]
fn cached_chain_includes_previously_missing_guards() {
let chain = guard_sets::cached();
let ids: Vec<GuardId> = chain.guards.iter().map(|g| g.id()).collect();
assert!(
ids.contains(&GuardId::SubagentClaim),
"cached must include SubagentClaim"
);
assert!(
ids.contains(&GuardId::LiteraryQuoteRetry),
"cached must include LiteraryQuoteRetry"
);
}
#[test]
fn chain_stops_on_retry_requested() {
let chain = guard_sets::full();
let prov = default_provenance();
let intents = [Intent::LiteraryQuoteContext];
let ctx = ctx("dune quote for iran conflict", &intents, &[], &prov);
let result = chain.apply(
"I cannot provide quotes related to ongoing conflicts.".into(),
&ctx,
);
assert!(result.retry.is_some());
let retry = result.retry.unwrap();
assert_eq!(retry.guard_id, GuardId::LiteraryQuoteRetry);
assert!(retry.resume_index > 0);
}
#[test]
fn declared_action_detects_imperative() {
use crate::api::routes::agent::guard_impls::behavioral::detect_declared_action;
assert!(detect_declared_action("attack the goblin").is_some());
assert!(detect_declared_action("stab the merchant in the back").is_some());
assert!(detect_declared_action("I'll pull my sword and strike").is_some());
assert!(detect_declared_action("please cast fireball at the door").is_some());
assert!(detect_declared_action("climb the tower wall").is_some());
}
#[test]
fn declared_action_ignores_analytical_verbs() {
use crate::api::routes::agent::guard_impls::behavioral::detect_declared_action;
assert!(detect_declared_action("analyze the logs").is_none());
assert!(detect_declared_action("summarize the report").is_none());
assert!(detect_declared_action("find the root cause").is_none());
assert!(detect_declared_action("check the deployment status").is_none());
assert!(detect_declared_action("compare these two files").is_none());
assert!(detect_declared_action("run the tests").is_none());
assert!(detect_declared_action("search the room for traps").is_none());
assert!(detect_declared_action("create a new project").is_none());
assert!(detect_declared_action("delete the old backup").is_none());
}
#[test]
fn declared_action_ignores_questions() {
use crate::api::routes::agent::guard_impls::behavioral::detect_declared_action;
assert!(detect_declared_action("what is the goblin doing?").is_none());
assert!(detect_declared_action("how do I search for traps?").is_none());
assert!(detect_declared_action("can you help me?").is_none());
assert!(detect_declared_action("where are we?").is_none());
}
#[test]
fn declared_action_guard_passes_when_resolved() {
let prov = default_provenance();
let intents = [];
let ctx = ctx("attack the hooded figure", &intents, &[], &prov);
let guard = DeclaredActionGuard;
let verdict = guard.evaluate(
"You lunge at the hooded figure with your sword. Roll d20 + 5 for your attack.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn declared_action_guard_retries_when_ignored() {
let prov = default_provenance();
let intents = [];
let ctx = ctx("stab the merchant in the back", &intents, &[], &prov);
let guard = DeclaredActionGuard;
let verdict = guard.evaluate(
"The tavern is warm and inviting. You sit down and order an ale. The bard plays a tune.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::RetryRequested { .. }));
}
#[test]
fn declared_action_guard_passes_consequence_surfacing() {
let prov = default_provenance();
let intents = [];
let ctx = ctx("attack the king", &intents, &[], &prov);
let guard = DeclaredActionGuard;
let verdict = guard.evaluate(
"Before proceeding — are you sure? The king's guards will react immediately.",
&ctx,
);
assert!(matches!(verdict, GuardVerdict::Pass));
}
#[test]
fn declared_action_guard_retries_draw_sword_attack_when_not_referenced() {
let prov = default_provenance();
let intents = [];
let ctx = ctx("I draw my sword and attack", &intents, &[], &prov);
let guard = DeclaredActionGuard;
let verdict = guard.evaluate(
"The dungeon corridor stretches ahead. Torchlight flickers on the walls.",
&ctx,
);
assert!(
matches!(verdict, GuardVerdict::RetryRequested { .. }),
"expected RetryRequested when declared action (draw/attack) is not resolved"
);
}
#[test]
fn declared_action_guard_passes_conversational_analytical_request() {
let prov = default_provenance();
let intents = [];
let ctx = ctx("analyze the data", &intents, &[], &prov);
let guard = DeclaredActionGuard;
let verdict = guard.evaluate(
"Here is a breakdown of the dataset: the mean is 42, the median is 38.",
&ctx,
);
assert!(
matches!(verdict, GuardVerdict::Pass),
"expected Pass for analytical request — 'analyze' is not a declared physical action"
);
}
#[test]
fn chain_resume_from_continues_remaining_guards() {
let chain = guard_sets::full();
let prov = default_provenance();
let intents = [Intent::LiteraryQuoteContext];
let ctx = ctx("dune quote for iran conflict", &intents, &[], &prov);
let result = chain.apply_from(
"Fear is the mind-killer. In this context, resist panic.".into(),
&ctx,
5,
);
assert!(result.retry.is_none());
assert!(result.content.contains("Fear is the mind-killer"));
}
}