use crate::tools::recall_projection::{
apply_score_floor, candidate_window, include_creator_tags_arg, min_score_arg, serialize_recall,
RecallProjection, MAX_CANDIDATE_WINDOW,
};
use serde_json::json;
use trusty_common::memory_core::palace::Drawer;
use trusty_common::memory_core::retrieval::RecallResult;
use uuid::Uuid;
const CREATOR_TAGS: [&str; 4] = [
"creator:client=claude-code",
"creator:version=0.26.1",
"creator:source=mcp",
"creator:cwd=/Users/masa/projects/trusty-tools",
];
fn hit(layer: u8, score: f32, topical: &[&str]) -> RecallResult {
let mut drawer = Drawer::new(Uuid::nil(), "recall projection fixture body");
drawer.importance = 0.5;
drawer.tags = topical
.iter()
.map(|t| (*t).to_string())
.chain(CREATOR_TAGS.iter().map(|t| (*t).to_string()))
.collect();
RecallResult {
drawer,
score,
layer,
}
}
fn tag_arrays(response: &serde_json::Value) -> Vec<Vec<String>> {
response["results"]
.as_array()
.expect("results array")
.iter()
.map(|r| {
r["tags"]
.as_array()
.expect("tags array")
.iter()
.map(|t| t.as_str().expect("tag string").to_string())
.collect()
})
.collect()
}
fn no_floor(include_creator_tags: bool) -> RecallProjection {
RecallProjection {
include_creator_tags,
dropped_below_floor: 0,
}
}
#[test]
fn creator_tags_are_hidden_by_default() {
let response = serialize_recall(
"alpha",
"q",
vec![hit(2, 0.9, &["rust", "retrieval"])],
&no_floor(false),
);
assert_eq!(tag_arrays(&response), vec![vec!["rust", "retrieval"]]);
}
#[test]
fn creator_tags_come_back_when_the_flag_is_set() {
let response = serialize_recall(
"alpha",
"q",
vec![hit(2, 0.9, &["rust", "retrieval"])],
&no_floor(true),
);
let mut expected = vec!["rust".to_string(), "retrieval".to_string()];
expected.extend(CREATOR_TAGS.iter().map(|t| (*t).to_string()));
assert_eq!(tag_arrays(&response), vec![expected]);
}
#[test]
fn score_floor_drops_below_and_keeps_above() {
let mut results = vec![hit(2, 0.46, &["kept"]), hit(2, 0.38, &["dropped"])];
let dropped = apply_score_floor(&mut results, Some(0.4), 10);
assert_eq!(dropped, 1);
assert_eq!(results.len(), 1);
assert_eq!(results[0].drawer.tags[0], "kept");
}
#[test]
fn score_floor_keeps_identity_and_essential_layers() {
let mut results = vec![
hit(0, 0.10, &["identity"]),
hit(1, 0.25, &["essential"]),
hit(2, 0.30, &["semantic"]),
];
let dropped = apply_score_floor(&mut results, Some(0.4), 10);
assert_eq!(dropped, 1);
let survivors: Vec<&str> = results.iter().map(|r| r.drawer.tags[0].as_str()).collect();
assert_eq!(survivors, vec!["identity", "essential"]);
}
#[test]
fn score_floor_applies_before_top_k() {
let mut results = vec![
hit(2, 0.90, &["a"]),
hit(2, 0.35, &["b"]),
hit(2, 0.80, &["c"]),
hit(2, 0.30, &["d"]),
hit(2, 0.70, &["e"]),
hit(2, 0.20, &["f"]),
];
let dropped = apply_score_floor(&mut results, Some(0.4), 3);
assert_eq!(dropped, 3);
let kept: Vec<&str> = results.iter().map(|r| r.drawer.tags[0].as_str()).collect();
assert_eq!(kept, vec!["a", "c", "e"]);
}
#[test]
fn recall_response_reports_the_dropped_count() {
let mut results = vec![hit(2, 0.46, &["kept"]), hit(2, 0.38, &["gone"])];
let dropped_below_floor = apply_score_floor(&mut results, Some(0.4), 10);
let filtered = serialize_recall(
"alpha",
"q",
results,
&RecallProjection {
include_creator_tags: false,
dropped_below_floor,
},
);
assert_eq!(filtered["dropped_below_floor"], json!(1));
let unfiltered = serialize_recall("alpha", "q", vec![hit(2, 0.9, &["x"])], &no_floor(false));
assert_eq!(unfiltered["dropped_below_floor"], json!(0));
}
#[test]
fn min_score_arg_is_absent_by_default() {
let args = json!({ "query": "anything" });
assert_eq!(
min_score_arg(&args, "memory_recall").expect("no floor"),
None
);
assert!(!include_creator_tags_arg(&args));
let nulled = json!({ "min_score": null });
assert_eq!(
min_score_arg(&nulled, "memory_recall").expect("null is absent"),
None
);
assert_eq!(
min_score_arg(&json!({ "min_score": 0.4 }), "memory_recall").expect("numeric"),
Some(0.4)
);
assert_eq!(
min_score_arg(&json!({ "min_score": 1 }), "memory_recall").expect("integer"),
Some(1.0)
);
}
#[test]
fn min_score_arg_rejects_a_non_numeric_value() {
let err = min_score_arg(&json!({ "min_score": true }), "memory_recall")
.expect_err("a bool floor must be rejected");
assert!(
err.to_string()
.contains("memory_recall: 'min_score' must be a number"),
"unexpected message: {err}"
);
assert!(min_score_arg(&json!({ "min_score": [0.4] }), "memory_recall_deep").is_err());
}
#[test]
fn min_score_arg_rejects_a_numeric_string() {
let err = min_score_arg(&json!({ "min_score": "0.4" }), "memory_recall")
.expect_err("a quoted floor must be rejected, not coerced");
assert!(
err.to_string().contains("must be a number"),
"unexpected message: {err}"
);
}
#[test]
fn candidate_window_is_top_k_without_a_floor() {
assert_eq!(candidate_window(10, None), 10);
assert_eq!(candidate_window(500, None), 500);
}
#[test]
fn candidate_window_widens_and_caps_with_a_floor() {
assert_eq!(candidate_window(3, Some(0.4)), 12);
assert_eq!(candidate_window(10, Some(0.4)), 40);
assert_eq!(candidate_window(500, Some(0.4)), 500);
assert!(candidate_window(100, Some(0.4)) <= MAX_CANDIDATE_WINDOW);
}
#[test]
fn a_widened_window_fills_top_k_after_the_floor() {
let top_k = 3;
let fetch_k = candidate_window(top_k, Some(0.4));
let mut candidates = vec![
hit(2, 0.90, &["a"]),
hit(2, 0.35, &["b"]),
hit(2, 0.80, &["c"]),
hit(2, 0.30, &["d"]),
hit(2, 0.70, &["e"]),
hit(2, 0.20, &["f"]),
];
assert!(
candidates.len() <= fetch_k,
"the widened window must hold every candidate: {} > {fetch_k}",
candidates.len()
);
let dropped = apply_score_floor(&mut candidates, Some(0.4), top_k);
assert_eq!(dropped, 3);
assert_eq!(candidates.len(), top_k);
let kept: Vec<&str> = candidates
.iter()
.map(|r| r.drawer.tags[0].as_str())
.collect();
assert_eq!(kept, vec!["a", "c", "e"]);
}
#[test]
fn eight_hit_recall_response_shrinks_without_creator_tags() {
let results: Vec<RecallResult> = (0..8)
.map(|i| hit(2, 0.9 - (i as f32) * 0.05, &["rust", "retrieval"]))
.collect();
let with_creator = serialize_recall("alpha", "q", results.clone(), &no_floor(true)).to_string();
let without_creator = serialize_recall("alpha", "q", results, &no_floor(false)).to_string();
println!(
"8-hit recall response: {} bytes with creator tags, {} bytes without ({} saved)",
with_creator.len(),
without_creator.len(),
with_creator.len() - without_creator.len()
);
assert!(
without_creator.len() * 10 <= with_creator.len() * 7,
"expected the default projection to cut at least 30% of the response: \
{} bytes vs {} bytes",
without_creator.len(),
with_creator.len()
);
}