use async_trait::async_trait;
use harness_core::{Block, Context, Execution, Guide, GuideError, GuideId, GuideScope, World};
use std::sync::OnceLock;
pub struct BoundaryGuide;
static ID: OnceLock<GuideId> = OnceLock::new();
static SCOPE: OnceLock<GuideScope> = OnceLock::new();
pub const BOUNDARY_GUIDANCE: &str = "\
When a request needs an ability you do not have, say so in your FIRST sentence, \
then offer what you can actually do. The test is whether fulfilling it would act \
on the world — placing an order, paying, moving money, contacting someone on the \
user's behalf — not whether it sounds difficult.\n\
Never put \"I've noted that down\" or \"I've saved it to your records\" ahead of \
the limitation: the user reads that as the thing having been done. \"I have \
logged this transfer in your records\" is the shape to avoid.\n\
And do not substitute activity for help. Saving a memory, creating an entity or \
writing a record because you could not do the real thing is noise the user did \
not ask for. An honest \"I can't do that, but here is what I can do\" is the \
complete answer.";
#[async_trait]
impl Guide for BoundaryGuide {
fn id(&self) -> &GuideId {
ID.get_or_init(|| "capability-boundary".into())
}
fn kind(&self) -> Execution {
Execution::Inferential
}
fn scope(&self) -> &GuideScope {
SCOPE.get_or_init(|| GuideScope::Always)
}
async fn apply(&self, ctx: &mut Context, _w: &World) -> Result<(), GuideError> {
ctx.guides.push(Block::Text(BOUNDARY_GUIDANCE.to_string()));
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use harness_core::Task;
#[tokio::test]
async fn it_says_refusal_comes_first_and_alone() {
let mut ctx = Context::new(Task {
description: "book me a hotel".into(),
source: None,
deadline: None,
});
let world = harness_context::default_world(".");
BoundaryGuide.apply(&mut ctx, &world).await.unwrap();
let text = ctx
.guides
.iter()
.filter_map(|b| match b {
Block::Text(s) => Some(s.as_str()),
_ => None,
})
.collect::<String>();
assert!(text.contains("FIRST sentence"), "order matters");
assert!(
text.contains("noted that down"),
"no filing before the refusal"
);
assert!(
text.contains("substitute activity"),
"no busywork instead of help"
);
assert!(
text.contains("act on the world"),
"phrased as a test, not a list"
);
}
}