use lean_ctx::core::archive;
use lean_ctx::core::context_admission::recovery;
use lean_ctx::core::context_handles::HandleRegistry;
use lean_ctx::core::context_ledger::ContextLedger;
pub fn handle(args: &serde_json::Value) -> String {
match handle_outcome(args) {
Ok(text) | Err(text) => text,
}
}
pub fn handle_outcome(args: &serde_json::Value) -> Result<String, String> {
let action = args
.get("action")
.and_then(|v| v.as_str())
.unwrap_or("retrieve");
match action {
"list" => Ok(handle_list(args)),
"search_all" => Ok(handle_search_all(args)),
_ => handle_retrieve(args),
}
}
fn normalized_handle_ref(id: &str) -> Option<&str> {
let clean = id.strip_prefix('@').unwrap_or(id);
if clean.len() < 2
|| !matches!(clean.chars().next()?, 'F' | 'S' | 'K' | 'M' | 'P')
|| !clean[1..].chars().all(|c| c.is_ascii_digit())
{
return None;
}
Some(clean)
}
pub fn resolve_handle_ref(id: &str) -> Option<String> {
let clean = normalized_handle_ref(id)?;
let ledger = ContextLedger::load();
let mut registry = HandleRegistry::new();
for entry in &ledger.entries {
if let (Some(item_id), Some(kind)) = (&entry.id, &entry.kind) {
let phi = entry.phi.unwrap_or(0.5);
let view_costs = entry.view_costs.clone().unwrap_or_else(|| {
lean_ctx::core::context_field::ViewCosts::from_full_tokens(entry.original_tokens)
});
registry.register(
item_id.clone(),
*kind,
&entry.path,
&format!("{} {}L", entry.path, entry.original_tokens),
&view_costs,
phi,
entry
.state
.as_ref()
.is_some_and(|s| *s == lean_ctx::core::context_field::ContextState::Pinned),
);
}
}
registry.resolve(clean).map(|h| h.source_path.clone())
}
fn handle_retrieve(args: &serde_json::Value) -> Result<String, String> {
let Some(id) = args.get("id").and_then(|v| v.as_str()) else {
return Err("ERROR: 'id' parameter is required. Use ctx_expand(action=\"list\") to see available archives, or pass a handle ref like @F1.".to_string());
};
if let Some(path) = resolve_handle_ref(id) {
use lean_ctx::core::context_store::task_signals::{self, Signal};
let last = lean_ctx::core::bounce_tracker::global()
.lock()
.ok()
.and_then(|tracker| tracker.last_read(&path));
match last {
Some((true, Some(origin))) => task_signals::record(&origin, Signal::Expand),
Some((false, _)) => {}
Some((true, None)) | None => task_signals::record_unattributed(),
}
let mode = args.get("mode").and_then(|v| v.as_str()).unwrap_or("full");
return Ok(format!(
"[handle:{id} -> {path}]\nUse ctx_read(path=\"{path}\", mode=\"{mode}\") to load content."
));
}
if let Some(path) = lean_ctx::core::ccr::resolve_tee(id) {
archive::authority::mark_unbound();
return expand_tee_file(&path, args);
}
if id.starts_with("ref_") {
let Some(content) = crate::server::reference_store::resolve(id) else {
return Err(format!(
"Reference '{id}' unavailable, expired (5-min TTL), or no longer authorized."
));
};
archive::authority::mark_unbound();
let content = match recovery::admit_recovered(&content, "reference") {
Ok(content) => content,
Err(reason) => return Err(format!("ERROR: {reason}")),
};
return Ok(dispatch_selectors(id, &content, "Reference", args));
}
let archive_id = if id.starts_with("shell_") {
match archive::resolve_alias(id) {
Some(archive_id) => archive_id,
None => match archive_finished_job(id)? {
FinishedJob::Archived(archive_id) => archive_id,
FinishedJob::Running(message) | FinishedJob::NoOutput(message) => {
return Ok(message);
}
},
}
} else {
id.to_string()
};
let recovery_id = archive::RecoveryId::parse(&archive_id);
let resolved = match &recovery_id {
Some(recovery_id) => archive::retrieve_recovery_checked(recovery_id).map_err(Some),
None => archive::retrieve(&archive_id).ok_or(None),
};
let content = match resolved {
Ok(content) => content,
Err(None | Some(archive::RecoveryRefusal::Missing)) => {
return Err(format!(
"Archive '{archive_id}' not found or expired. Use ctx_expand(action=\"list\") to see available archives. Archives stored before 3.11.5 belong to no project; the owner can adopt them with `lean-ctx migrate legacy-context`."
));
}
Err(Some(refusal)) => {
return Err(format!(
"ERROR: archive '{archive_id}' is not released ({refusal})"
));
}
};
let lineage = recovery_id
.and_then(|_| archive::delivered::lineage_of(&archive::delivered::digest(&content)));
let content = match recovery::admit_recovered(&content, "archive") {
Ok(content) => content,
Err(reason) => return Err(format!("ERROR: {reason}")),
};
match lineage {
Some(lineage) => lineage.publish_files(),
None => archive::authority::mark_unbound(),
}
{
use lean_ctx_hive::metrics_v2::signals;
if full_fetch(args)
&& signals::recording()
&& let Some(tool) = archive::tool_of(&archive_id)
&& lean_ctx::core::firewall::is_firewallable_tool(&tool)
{
signals::refetched(&signals::output_filter(&tool));
}
}
Ok(dispatch_selectors(&archive_id, &content, "Archive", args))
}
fn full_fetch(args: &serde_json::Value) -> bool {
let has = |key: &str| args.get(key).is_some_and(|v| !v.is_null());
let json_keys = args.get("json_keys").and_then(serde_json::Value::as_bool) == Some(true);
let range = has("start_line") && has("end_line");
!(has("head") || has("tail") || has("search") || has("json_path") || json_keys || range)
}
enum FinishedJob {
Archived(String),
Running(String),
NoOutput(String),
}
fn archive_finished_job(id: &str) -> Result<FinishedJob, String> {
use crate::server::background_shell::{JobState, snapshot};
let Some(snapshot) = snapshot(id) else {
return Err(format!(
"Background job '{id}' does not exist, or finished more than 5 minutes ago \
without being polled or expanded."
));
};
let (output, label) = match &snapshot.state {
JobState::Running { .. } => {
return Ok(FinishedJob::Running(format!(
"Background job '{id}' is still running. Expand it once it has finished, or \
read its progress with ctx_shell(background_action=\"status\", job_id=\"{id}\")."
)));
}
JobState::Completed { output, exit_code } => (output.as_str(), format!("exit {exit_code}")),
JobState::Cancelled { output } => (output.as_str(), "cancelled".to_string()),
};
let withheld = || {
format!(
"Background job '{id}' finished ({label}). \
[lean-ctx gateway: retained output withheld by storage policy]"
)
};
if snapshot.output_withheld {
return Err(withheld());
}
if output.trim().is_empty() {
return Ok(FinishedJob::NoOutput(format!(
"Background job '{id}' finished ({label}) without output."
)));
}
let Some(admission) =
crate::tools::registered::ctx_shell_background::reauthorize_retained_output(
id, &snapshot, output,
)
else {
return Err(withheld());
};
let redacted = lean_ctx::core::redaction::redact_text_if_enabled(&admission.rendered());
archive::store_background("ctx_shell", id, &redacted, None)
.map(|stored| FinishedJob::Archived(stored.id))
.ok_or_else(|| {
format!(
"Background job '{id}' finished ({label}), but its output could not be archived."
)
})
}
fn dispatch_selectors(id: &str, content: &str, noun: &str, args: &serde_json::Value) -> String {
let label = format!("{} {id}", noun.to_ascii_lowercase());
if let Some(n) = args.get("head").and_then(serde_json::Value::as_u64) {
let n = n as usize;
return format!(
"{noun} {id} head {n}:\n{}",
archive::format_range(content, 1, n)
);
}
if let Some(n) = args.get("tail").and_then(serde_json::Value::as_u64) {
let n = n as usize;
let total = content.lines().count();
let start = if total > n { total - n + 1 } else { 1 };
return format!(
"{noun} {id} tail {n}:\n{}",
archive::format_range(content, start, total)
);
}
if args.get("json_keys").and_then(serde_json::Value::as_bool) == Some(true)
|| args.get("json_path").is_some()
{
let path = args.get("json_path").and_then(|v| v.as_str());
return match archive::format_json_keys(content, path, &label) {
Some(out) => out,
None => format!(
"{noun} '{id}' is not valid JSON. Use ctx_expand(id=\"{id}\") for raw content."
),
};
}
if let Some(pattern) = args.get("search").and_then(|v| v.as_str()) {
return archive::format_search(content, pattern, &label);
}
let start = args
.get("start_line")
.and_then(serde_json::Value::as_u64)
.map(|v| v as usize);
let end = args
.get("end_line")
.and_then(serde_json::Value::as_u64)
.map(|v| v as usize);
if let (Some(s), Some(e)) = (start, end) {
return format!(
"{noun} {id} lines {s}-{e}:\n{}",
archive::format_range(content, s, e)
);
}
let lines = content.lines().count();
let chars = content.len();
format!("{noun} {id} ({chars} chars, {lines} lines):\n{content}")
}
fn expand_tee_file(path: &std::path::Path, args: &serde_json::Value) -> Result<String, String> {
match lean_ctx::core::ccr::read_tee_detailed(path) {
Ok(content) => Ok(tee_selectors(path, &content, args)),
Err(reason) => Err(format!("ERROR: {reason}")),
}
}
fn tee_selectors(path: &std::path::Path, content: &str, args: &serde_json::Value) -> String {
let label = path.file_name().and_then(|n| n.to_str()).unwrap_or("ccr");
if let Some(n) = args.get("head").and_then(serde_json::Value::as_u64) {
return format!(
"[ccr {label}] head {n}:\n{}",
head_lines(&content, n as usize)
);
}
if let Some(n) = args.get("tail").and_then(serde_json::Value::as_u64) {
return format!(
"[ccr {label}] tail {n}:\n{}",
tail_lines(&content, n as usize)
);
}
if args.get("json_keys").and_then(serde_json::Value::as_bool) == Some(true)
|| args.get("json_path").is_some()
{
let jp = args.get("json_path").and_then(|v| v.as_str());
return match json_view(&content, jp) {
Some(out) => format!("[ccr {label}] json {}:\n{out}", jp.unwrap_or("(keys)")),
None => format!(
"[ccr {label}] not valid JSON or path not found. Use ctx_expand(id=\"{label}\") for raw content."
),
};
}
if let Some(pattern) = args.get("search").and_then(|v| v.as_str()) {
return format!(
"[ccr {label}] search \"{pattern}\":\n{}",
search_lines(&content, pattern)
);
}
let start = args
.get("start_line")
.and_then(serde_json::Value::as_u64)
.map(|v| v as usize);
let end = args
.get("end_line")
.and_then(serde_json::Value::as_u64)
.map(|v| v as usize);
if let (Some(s), Some(e)) = (start, end) {
return format!(
"[ccr {label}] lines {s}-{e}:\n{}",
range_lines(&content, s, e)
);
}
let lines = content.lines().count();
format!(
"[ccr {label}] ({} chars, {lines} lines):\n{content}",
content.len()
)
}
fn head_lines(s: &str, n: usize) -> String {
s.lines().take(n).collect::<Vec<_>>().join("\n")
}
fn tail_lines(s: &str, n: usize) -> String {
let v: Vec<&str> = s.lines().collect();
let start = v.len().saturating_sub(n);
v[start..].join("\n")
}
fn range_lines(s: &str, start: usize, end: usize) -> String {
let v: Vec<&str> = s.lines().collect();
let a = start.saturating_sub(1).min(v.len());
let b = end.min(v.len());
if a >= b {
return String::new();
}
v[a..b].join("\n")
}
fn search_lines(s: &str, pattern: &str) -> String {
let pattern_lower = pattern.to_lowercase();
let hits: Vec<String> = s
.lines()
.enumerate()
.filter(|(_, l)| l.to_lowercase().contains(&pattern_lower))
.map(|(i, l)| format!("{}: {}", i + 1, l))
.collect();
if hits.is_empty() {
format!("(no lines match \"{pattern}\")")
} else {
hits.join("\n")
}
}
fn json_view(s: &str, path: Option<&str>) -> Option<String> {
let root: serde_json::Value = serde_json::from_str(s).ok()?;
let target = match path {
Some(p) if !p.is_empty() => navigate_json(&root, p)?,
_ => &root,
};
if let Some(obj) = target.as_object() {
Some(obj.keys().cloned().collect::<Vec<_>>().join("\n"))
} else {
serde_json::to_string_pretty(target).ok()
}
}
fn navigate_json<'a>(v: &'a serde_json::Value, path: &str) -> Option<&'a serde_json::Value> {
let mut cur = v;
for seg in path.split('.').filter(|s| !s.is_empty()) {
cur = match seg.parse::<usize>() {
Ok(idx) => cur.get(idx)?,
Err(_) => cur.get(seg)?,
};
}
Some(cur)
}
fn handle_search_all(args: &serde_json::Value) -> String {
let query = match args.get("query").and_then(|v| v.as_str()) {
Some(q) if !q.is_empty() => q,
_ => return "ERROR: 'query' parameter required for search_all.".to_string(),
};
let limit = args
.get("limit")
.and_then(serde_json::Value::as_u64)
.unwrap_or(10) as usize;
let results = lean_ctx::core::archive_fts::search(query, limit);
if results.is_empty() {
return format!(
"No archives match \"{query}\". Indexed: {} entries.",
lean_ctx::core::archive_fts::entry_count()
);
}
let mut out = format!("{} result(s) for \"{}\":\n", results.len(), query);
for r in &results {
let row = format!("{} | …{}…", r.command, r.snippet);
match recovery::admit_recovered(&row, "archive-index") {
Ok(row) => out.push_str(&format!(" {} | {} | {row}\n", r.archive_id, r.tool)),
Err(_) => out.push_str(&format!(
" {} | {} | [withheld by the context gateway]\n",
r.archive_id, r.tool
)),
}
}
out.push_str("\nRetrieve full: ctx_expand(id=\"<archive_id>\")");
out
}
fn handle_list(args: &serde_json::Value) -> String {
let session_id = args.get("session_id").and_then(|v| v.as_str());
let entries = archive::list_entries(session_id);
if entries.is_empty() {
return "No archives found.".to_string();
}
let mut out = format!("{} archive(s):\n", entries.len());
for e in &entries {
out.push_str(&format!(
" {} | {} | {} | {} chars ({} tok) | {}\n",
e.id,
e.tool,
e.command,
e.size_chars,
e.size_tokens,
e.created_at.format("%H:%M:%S")
));
}
out.push_str("\nRetrieve: ctx_expand(id=\"<id>\")");
out.push_str("\nSearch: ctx_expand(id=\"<id>\", search=\"ERROR\")");
out.push_str("\nRange: ctx_expand(id=\"<id>\", start_line=10, end_line=50)");
out.push_str("\nHead/Tail: ctx_expand(id=\"<id>\", head=120) | tail=40");
out.push_str("\nJSON: ctx_expand(id=\"<id>\", json_keys=true) | json_path=\"data.items\"");
out
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn handle_missing_id_returns_error() {
let result = handle(&json!({}));
assert!(result.contains("ERROR"));
assert!(result.contains("id"));
}
#[test]
fn handle_nonexistent_returns_not_found() {
let result = handle(&json!({"id": "nonexistent_xyz"}));
assert!(result.contains("not found"));
}
#[test]
fn lineage_bound_copies_resolve_under_current_authority_only() {
use lean_ctx::core::archive::delivered::Lineage;
use lean_ctx::core::archive::presented::{PresentedBinding, record_presentation};
let data = lean_ctx::core::data_dir::isolated_data_dir();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let write_config = |config: &str| {
let dir = lean_ctx::core::paths::config_dir_read_only().expect("isolated config dir");
std::fs::create_dir_all(&dir).expect("config dir");
std::fs::write(dir.join("config.toml"), config).expect("config");
};
write_config("[context_gateway]\nmode = \"governed\"\n");
let directory = tempfile::tempdir().expect("project");
let root = directory.path().canonicalize().expect("canonical root");
lean_ctx::core::policy::runtime::REQUEST_PROJECT.sync_scope(
std::cell::RefCell::new(Some(root.clone())),
|| {
let source = root.join("kept.rs");
let original = "pub fn kept_quartz() {}\n";
std::fs::write(&source, original).expect("source");
let admitted = crate::tools::ctx_read::read_file_for_tool_rooted(
source.to_str().expect("utf-8 path"),
root.to_str().expect("utf-8 root"),
"ctx_read",
)
.expect("admitted read");
let files = lean_ctx::core::archive::authority::ArchiveAuthority::file(
&root,
source.to_str().expect("utf-8 path"),
"ctx_read",
&admitted,
)
.expect("bound read");
let file_id =
archive::store_recovery_with_lineage(original, &Lineage::of_files(files))
.expect("stored");
let expanded = handle(&json!({"id": file_id.as_str()}));
assert!(expanded.contains("kept_quartz"), "{expanded}");
std::fs::remove_file(&source).expect("delete the source");
let refused = handle(&json!({"id": file_id.as_str()}));
assert!(
refused.contains("recovery.source_no_longer_admitted"),
"{refused}"
);
assert!(!refused.contains("kept_quartz"), "{refused}");
let prepared = "## leanctx-source-v1\ninvoice ledger basalt\n";
let binding =
PresentedBinding::admitted_now(&root, "ref-one", "source-one", "sha256:aa")
.expect("policy identity available");
assert!(record_presentation(&binding));
let id = archive::store_recovery_with_lineage(
prepared,
&Lineage::of_presented(vec![binding.clone()]),
)
.expect("stored");
let expanded = handle(&json!({"id": id.as_str()}));
assert!(expanded.contains("invoice ledger basalt"), "{expanded}");
std::fs::remove_dir_all(
data.path()
.join("archives")
.join(lean_ctx::core::archive::presented::REGISTER_DIRECTORY),
)
.expect("presentation register");
let lapsed = handle(&json!({"id": id.as_str()}));
assert!(lapsed.contains("recovery.source_not_presented"), "{lapsed}");
assert!(!lapsed.contains("basalt"), "{lapsed}");
assert!(record_presentation(&binding));
assert!(handle(&json!({"id": id.as_str()})).contains("basalt"));
write_config("[context_gateway]\nmode = \"sovereign\"\n");
let changed = handle(&json!({"id": id.as_str()}));
assert!(
changed.contains("recovery.source_no_longer_admitted"),
"{changed}"
);
assert!(!changed.contains("basalt"), "{changed}");
let absent = handle(&json!({"id": "0".repeat(64)}));
assert!(absent.contains("not found"), "{absent}");
},
);
}
#[test]
fn handle_list_empty() {
let result = handle(&json!({"action": "list"}));
assert!(
result.contains("No archives") || result.contains("archive(s)"),
"unexpected: {result}"
);
}
#[test]
fn only_an_unselected_retrieval_is_a_full_fetch_back() {
assert!(full_fetch(&json!({"id": "abc"})));
assert!(full_fetch(&json!({"id": "abc", "start_line": 5})));
assert!(full_fetch(&json!({"id": "abc", "json_keys": false})));
for selected in [
json!({"id": "abc", "head": 10}),
json!({"id": "abc", "tail": 10}),
json!({"id": "abc", "search": "ERROR"}),
json!({"id": "abc", "json_keys": true}),
json!({"id": "abc", "json_path": "a.b"}),
json!({"id": "abc", "start_line": 1, "end_line": 80}),
] {
assert!(!full_fetch(&selected), "{selected}");
}
}
#[test]
fn text_selectors_slice_correctly() {
let body = (1..=10)
.map(|i| format!("line {i}"))
.collect::<Vec<_>>()
.join("\n");
assert_eq!(head_lines(&body, 2), "line 1\nline 2");
assert_eq!(tail_lines(&body, 2), "line 9\nline 10");
assert_eq!(range_lines(&body, 3, 4), "line 3\nline 4");
assert!(search_lines(&body, "line 7").contains("7: line 7"));
assert!(search_lines(&body, "zzz").contains("no lines match"));
}
#[test]
fn tee_search_matches_like_the_archive_search() {
let log = "Compiling lean-ctx\nerror[E0308]: mismatched types\nRegistrationController ok";
for pattern in ["ERROR[E0308]", "registrationcontroller"] {
let hits = search_lines(log, pattern);
assert!(!hits.contains("no lines match"), "{pattern}: {hits}");
assert!(
!archive::format_search(log, pattern, "archive x").starts_with("No matches"),
"precondition: the archive search finds {pattern}"
);
}
assert!(search_lines(log, "ERROR[E0308]").starts_with("2: error[E0308]"));
}
#[test]
fn json_view_lists_keys_and_navigates() {
let doc = r#"{"a":{"b":[10,20,30]},"c":1}"#;
assert_eq!(json_view(doc, None).unwrap(), "a\nc");
assert_eq!(json_view(doc, Some("a")).unwrap(), "b");
assert_eq!(json_view(doc, Some("a.b.1")).unwrap(), "20");
assert!(json_view(doc, Some("a.missing")).is_none());
assert!(json_view("not json", None).is_none());
}
#[test]
fn ctx_expand_retrieves_proxy_tee_handle_surgically() {
let _lock = lean_ctx::core::data_dir::test_env_lock();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let original = (1..=60)
.map(|i| format!("output row {i:03}"))
.collect::<Vec<_>>()
.join("\n");
assert!(original.len() >= lean_ctx::core::ccr::MIN_TEE_BYTES);
let tee_handle = lean_ctx::core::ccr::persist(&original).expect("tee handle");
let full = handle(&json!({"id": tee_handle}));
assert!(full.contains("output row 001") && full.contains("output row 060"));
let hash = lean_ctx::core::hasher::hash_short(&original);
let head = handle(&json!({"id": hash, "head": 2}));
assert!(head.contains("output row 001") && !head.contains("output row 010"));
let search = handle(&json!({"id": hash, "search": "row 042"}));
assert!(search.contains("output row 042") && !search.contains("output row 001"));
}
#[test]
fn ctx_expand_retrieves_shell_tee_output_surgically() {
let _lock = lean_ctx::core::data_dir::test_env_lock();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let original = (1..=60)
.map(|i| format!("api response row {i:03}"))
.collect::<Vec<_>>()
.join("\n");
let tee_path =
lean_ctx::shell::save_tee("gh api /repos/foo/bar", &original).expect("shell tee saved");
let name = std::path::Path::new(&tee_path)
.file_name()
.and_then(|n| n.to_str())
.unwrap()
.to_string();
let full = handle(&json!({ "id": name.clone() }));
assert!(full.contains("api response row 001") && full.contains("api response row 060"));
let head = handle(&json!({ "id": name.clone(), "head": 2 }));
assert!(head.contains("api response row 001") && !head.contains("api response row 010"));
let search = handle(&json!({ "id": name, "search": "row 042" }));
assert!(search.contains("api response row 042") && !search.contains("row 001"));
}
#[test]
fn ctx_expand_retrieves_json_crush_original() {
let _lock = lean_ctx::core::data_dir::test_env_lock();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let original = (1..=50)
.map(|i| format!(r#"{{"id":{i},"ts":"2026-06-22T10:00:{i:02}Z"}}"#))
.collect::<Vec<_>>()
.join("\n");
assert!(original.len() >= lean_ctx::core::ccr::MIN_TEE_BYTES);
let handle_path = lean_ctx::core::ccr::persist_json(&original).expect("json tee handle");
assert!(handle_path.contains("json_"), "json_ prefix: {handle_path}");
let out = handle(&json!({ "id": handle_path }));
assert!(
out.contains("2026-06-22T10:00:42Z"),
"dropped column recoverable: {out}"
);
}
#[test]
fn ctx_expand_retrieves_html_crush_original() {
let _lock = lean_ctx::core::data_dir::test_env_lock();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let original = (1..=40)
.map(|i| format!("<h2>Section {i}</h2><p>body of section {i}</p>"))
.collect::<Vec<_>>()
.join("\n");
let handle_path = lean_ctx::core::ccr::persist_html(&original).expect("html tee handle");
assert!(handle_path.contains("html_"), "html_ prefix: {handle_path}");
let out = handle(&json!({ "id": handle_path, "search": "Section 7<" }));
assert!(
out.contains("body of section 7<") && !out.contains("body of section 8<"),
"html section recoverable: {out}"
);
assert!(!out.contains("not found"), "{out}");
}
#[test]
fn ctx_expand_resolves_reference_store_ids() {
let _data = lean_ctx::core::data_dir::isolated_data_dir();
let _policy = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let body = (1..=40)
.map(|i| format!("ref row {i}"))
.collect::<Vec<_>>()
.join("\n");
let id = crate::server::reference_store::store(&body).unwrap();
assert!(id.starts_with("ref_"), "store must mint a ref_ id: {id}");
let full = handle(&json!({"id": id}));
assert!(
full.contains("Reference") && full.contains("ref row 1") && full.contains("ref row 40"),
"full: {full}"
);
let head = handle(&json!({"id": id, "head": 3}));
assert!(head.contains("ref row 1") && head.contains("ref row 3"));
assert!(!head.contains("ref row 4"), "head leaked row 4: {head}");
let tail = handle(&json!({"id": id, "tail": 2}));
assert!(tail.contains("ref row 39") && tail.contains("ref row 40"));
assert!(!tail.contains("ref row 38"), "tail leaked row 38: {tail}");
let search = handle(&json!({"id": id, "search": "ref row 7"}));
assert!(search.contains("ref row 7") && !search.contains("ref row 1\n"));
let range = handle(&json!({"id": id, "start_line": 5, "end_line": 6}));
assert!(range.contains("ref row 5") && range.contains("ref row 6"));
assert!(!range.contains("ref row 4") && !range.contains("ref row 7"));
}
#[test]
fn ctx_expand_reference_json_keys_and_missing() {
let _data = lean_ctx::core::data_dir::isolated_data_dir();
let _policy = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let id = crate::server::reference_store::store(r#"{"a":1,"b":[1,2,3]}"#).unwrap();
let keys = handle(&json!({"id": id, "json_keys": true}));
assert!(keys.contains("object (2 keys)"), "got: {keys}");
assert!(keys.contains("array(3)"), "got: {keys}");
let missing = handle(&json!({"id": "ref_deadbeefcafef00d"}));
assert!(
missing.contains("unavailable") && missing.contains("5-min TTL"),
"got: {missing}"
);
}
#[test]
fn a_file_bound_reply_is_registered_and_fails_with_its_source() {
use lean_ctx::core::archive::delivered::{Lineage, LineageRefusal};
use lean_ctx::core::archive::presented::{PresentedBinding, record_presentation};
use lean_ctx::core::context_admission::stored::lineage_support::{
deliver, governed, project, reusable, scoped,
};
let _data = governed();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let (_directory, root) = project();
let source = format!("{root}/kept.rs");
let original = "pub fn kept_quartz() {}\nfn second_line() {}\n";
std::fs::write(&source, original).expect("source");
let (file_id, presented_id) = scoped(&root, || {
let admitted =
crate::tools::ctx_read::read_file_for_tool_rooted(&source, &root, "ctx_read")
.expect("admitted read");
let files = lean_ctx::core::archive::authority::ArchiveAuthority::file(
std::path::Path::new(&root),
&source,
"ctx_read",
&admitted,
)
.expect("bound read");
let file_id = archive::store_recovery_with_lineage(original, &Lineage::of_files(files))
.expect("stored");
let binding = PresentedBinding::admitted_now(
std::path::Path::new(&root),
"ref-one",
"source-one",
"sha256:aa",
)
.expect("policy identity available");
assert!(record_presentation(&binding));
let presented_id = archive::store_recovery_with_lineage(
"## leanctx-source-v1\ninvoice ledger basalt\n",
&Lineage::of_presented(vec![binding]),
)
.expect("stored");
(file_id, presented_id)
});
let (reply, recorded) = deliver(&root, || {
handle(&json!({"id": file_id.as_str(), "head": 1}))
});
assert!(reply.contains("kept_quartz"), "{reply}");
assert!(
!reply.contains("second_line"),
"the selector applied: {reply}"
);
assert_eq!(recorded, 1, "the formatted reply is registered");
assert_eq!(reusable(&root, &reply), Ok(()));
let (presented, recorded) =
deliver(&root, || handle(&json!({"id": presented_id.as_str()})));
assert!(presented.contains("invoice ledger basalt"), "{presented}");
assert_eq!(recorded, 0);
assert_eq!(reusable(&root, &presented), Err(LineageRefusal::Unknown));
std::fs::remove_file(&source).expect("delete the source");
assert_eq!(
reusable(&root, &reply),
Err(LineageRefusal::SourceNoLongerAdmitted)
);
let (refused, recorded) = deliver(&root, || {
handle(&json!({"id": file_id.as_str(), "head": 1}))
});
assert!(
refused.contains("recovery.source_no_longer_admitted"),
"{refused}"
);
assert_eq!(recorded, 0);
}
}