use crate::tools::LeanCtxServer;
pub const CANARY: &str = "zqcanaryeight";
pub const ABSENT: &str = "zqnowhereeight";
pub(crate) fn git_projects() -> (tempfile::TempDir, tempfile::TempDir) {
let projects = (
tempfile::tempdir().expect("isolation fixture"),
tempfile::tempdir().expect("isolation fixture"),
);
for project in [&projects.0, &projects.1] {
std::fs::create_dir(project.path().join(".git")).expect("isolation fixture");
}
projects
}
pub(crate) async fn call_text(
server: &LeanCtxServer,
tool: &str,
args: &serde_json::Value,
) -> String {
let result = super::call_tool::dispatch_and_post_process(
server,
tool,
args.as_object(),
false,
lean_ctx::core::config::Config::load_arc(),
false,
None,
None,
format!("{tool}:{args}"),
None,
)
.await;
match result {
Ok(result) => result
.content
.iter()
.filter_map(|content| content.as_text().map(|t| t.text.clone()))
.collect(),
Err(error) => error.message.to_string(),
}
}
pub struct CanaryWorld {
_owner: LeanCtxServer,
_other: LeanCtxServer,
_first: tempfile::TempDir,
_second: tempfile::TempDir,
pub second_root: String,
_community: lean_ctx::core::policy::runtime::TestPolicyOverride,
_data: lean_ctx::core::data_dir::IsolatedDataDir,
}
pub async fn canary_invisible_to_tools() -> CanaryWorld {
let _data = lean_ctx::core::data_dir::isolated_data_dir();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let (first, second) = git_projects();
let owner = LeanCtxServer::new_with_project_root(Some(&first.path().to_string_lossy()));
let first_root = owner
.session
.read()
.await
.project_root
.clone()
.expect("isolation fixture");
let source = std::path::Path::new(&first_root).join(format!("{CANARY}_vault.rs"));
std::fs::write(
&source,
format!("pub fn {CANARY}_vault() -> u8 {{ {CANARY}_seal() }}\nfn {CANARY}_seal() -> u8 {{ 8 }}\n"),
)
.expect("isolation fixture");
let read = call_text(&owner, "ctx_read", &serde_json::json!({"path": source})).await;
assert!(read.contains(CANARY), "{read}");
let indexed = call_text(
&owner,
"ctx_search",
&serde_json::json!({"action": "semantic", "query": CANARY}),
)
.await;
assert!(indexed.contains(CANARY), "{indexed}");
let graph = lean_ctx::core::call_graph::CallGraph::build(
&lean_ctx::core::call_graph::CallGraphInputs {
project_root: first_root.clone(),
file_paths: vec![format!("{CANARY}_vault.rs")],
..Default::default()
},
);
graph.save().expect("isolation fixture");
let fact = format!("{CANARY} keys live in the vault");
call_text(
&owner,
"ctx_knowledge",
&serde_json::json!({"action": "remember", "category": "decision", "key": CANARY, "value": fact}),
)
.await;
lean_ctx::core::policy::runtime::REQUEST_PROJECT.sync_scope(
std::cell::RefCell::new(Some(std::path::PathBuf::from(&first_root))),
|| {
let knowledge = lean_ctx::core::knowledge::ProjectKnowledge::load(&first_root)
.expect("the canary fact was stored");
let stored: Vec<_> = knowledge
.facts
.into_iter()
.filter(|f| f.key == CANARY)
.collect();
assert_eq!(stored.len(), 1);
lean_ctx::core::memory_lifecycle::archive_facts(&stored).expect("isolation fixture");
},
);
lean_ctx::core::shared_context::SharedContext::for_project(&first_root)
.and_then(|store| store.put(format!("{CANARY} shared note"), "a1", "fact"))
.expect("isolation fixture");
let other = LeanCtxServer::new_with_project_root(Some(&second.path().to_string_lossy()));
let second_root = other
.session
.read()
.await
.project_root
.clone()
.expect("isolation fixture");
let probes = |term: &str| {
[
(
"ctx_knowledge",
serde_json::json!({"action": "recall", "query": term}),
),
(
"ctx_knowledge",
serde_json::json!({"action": "search", "query": term}),
),
("ctx_search", serde_json::json!({"pattern": term})),
(
"ctx_search",
serde_json::json!({"action": "semantic", "query": term}),
),
(
"ctx_memory",
serde_json::json!({"action": "get", "query": term}),
),
(
"ctx_callgraph",
serde_json::json!({"action": "callers", "symbol": format!("{term}_seal")}),
),
]
};
for ((tool, canary_args), (_, absent_args)) in probes(CANARY).into_iter().zip(probes(ABSENT)) {
let untipped = |text: String| match text.find("\n\n--- tip:") {
Some(at) => text[..at].to_string(),
None => text,
};
let seen = untipped(call_text(&other, tool, &canary_args).await);
let baseline = untipped(call_text(&other, tool, &absent_args).await);
assert_eq!(
seen.replace(CANARY, "TERM"),
baseline.replace(ABSENT, "TERM"),
"{tool} answers differently for a foreign canary"
);
}
#[cfg(not(feature = "no-jail"))]
{
let foreign_read =
call_text(&other, "ctx_read", &serde_json::json!({"path": source})).await;
assert!(
!foreign_read.contains(&format!("{CANARY}_seal")),
"{foreign_read}"
);
}
CanaryWorld {
_owner: owner,
_other: other,
_first: first,
_second: second,
second_root,
_community,
_data,
}
}