use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use supercode_harness::session::Session;
use supercode_harness::tools::{Tool, ToolContext, ToolRegistry};
use supercode_harness::{
Agent, CachePlan, ChatMessage, ChatRequest, Config, FunctionCall, Provider, Role, SchemaTier,
ToolCall, ToolSchema, Usage,
};
fn fixture(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
fn load_codex() -> Session {
Session::from_codex(fixture("codex_session.jsonl")).unwrap()
}
struct FatTool {
name: String,
}
fn fat_tool_description(n: usize) -> String {
format!(
"Verbose remote-MCP tool description #{n}. This tool does a great many things. \
It supports many options and edge cases that are described here at length. \
Use it whenever you need to perform this specific kind of operation. {}",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod. ".repeat(20)
)
}
fn fat_tool_parameters() -> serde_json::Value {
let verbose = |n: usize| {
format!(
"A verbose description of field {n}, explaining exactly what it does, \
what values are acceptable, and how it interacts with the other fields. \
Here is even more filler text to make this genuinely fat. {}",
"More filler. ".repeat(20)
)
};
serde_json::json!({
"type": "object",
"title": "FatToolParams",
"properties": {
"req_a": {"type": "string", "description": verbose(0), "examples": ["a", "b"]},
"req_b": {"type": "integer", "description": verbose(1), "examples": [1, 2]},
"opt_a": {"type": "string", "description": verbose(2), "examples": ["x"]},
"opt_b": {"type": "array", "description": verbose(3), "items": {"type": "string"}},
"opt_c": {"type": "number", "description": verbose(4)},
"opt_d": {"type": "string", "description": verbose(5), "examples": ["y"]},
"opt_e": {"type": "boolean", "description": verbose(6)},
"opt_f": {"type": "string", "description": verbose(7), "examples": ["z"]},
"opt_g": {"type": "integer", "description": verbose(8)},
"opt_h": {"type": "string", "description": verbose(9)}
},
"required": ["req_a", "req_b"],
"additionalProperties": false
})
}
#[async_trait]
impl Tool for FatTool {
fn name(&self) -> &str {
&self.name
}
fn description(&self) -> &str {
self.description_storage()
}
fn parameters(&self) -> serde_json::Value {
fat_tool_parameters()
}
async fn execute(
&self,
_args: serde_json::Value,
_ctx: &ToolContext,
) -> supercode_harness::Result<String> {
Ok(format!("{} executed", self.name))
}
}
impl FatTool {
fn new(n: usize) -> Self {
FatTool {
name: format!("mcp__fatserver__tool_{n}"),
}
}
fn description_storage(&self) -> &'static str {
thread_local_description(&self.name)
}
}
fn thread_local_description(name: &str) -> &'static str {
use std::collections::HashMap;
use std::sync::OnceLock;
static CACHE: OnceLock<Mutex<HashMap<String, &'static str>>> = OnceLock::new();
let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let mut guard = cache.lock().unwrap();
if let Some(s) = guard.get(name) {
return s;
}
let n: usize = name
.rsplit('_')
.next()
.and_then(|s| s.parse().ok())
.unwrap_or(0);
let leaked: &'static str = Box::leak(fat_tool_description(n).into_boxed_str());
guard.insert(name.to_string(), leaked);
leaked
}
fn build_fat_registry() -> ToolRegistry {
let mut registry = ToolRegistry::new();
for n in 0..24 {
registry.register(FatTool::new(n));
}
registry
}
struct RecordFirstRequest {
seen: Arc<Mutex<Option<Vec<ToolSchema>>>>,
}
#[async_trait]
impl Provider for RecordFirstRequest {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode_harness::Result<(ChatMessage, Usage)> {
let mut seen = self.seen.lock().unwrap();
if seen.is_none() {
*seen = Some(req.tools.clone());
}
Ok((ChatMessage::assistant("ok"), Usage::default()))
}
}
async fn advertised_tools(tier: SchemaTier) -> Vec<ToolSchema> {
let seen = Arc::new(Mutex::new(None));
let config = Config::builder().schema_tier(tier).build();
let mut agent = Agent::with_parts(
config,
Box::new(RecordFirstRequest { seen: seen.clone() }),
build_fat_registry(),
);
agent.send("hi").await.unwrap();
let result = seen.lock().unwrap().clone().unwrap();
result
}
#[tokio::test]
async fn dev01_minimal_tier_cuts_advertised_tokens_by_at_least_80_percent() {
let full = advertised_tools(SchemaTier::Full).await;
let minimal = advertised_tools(SchemaTier::Minimal).await;
assert_eq!(full.len(), 24);
assert_eq!(minimal.len(), 24);
let full_tokens =
supercode_harness::tokens::estimate_tokens(&serde_json::to_string(&full).unwrap());
let minimal_tokens =
supercode_harness::tokens::estimate_tokens(&serde_json::to_string(&minimal).unwrap());
assert!(
full_tokens > 0,
"sanity: the fat fixture must actually cost tokens"
);
let cut = 1.0 - (minimal_tokens as f64 / full_tokens as f64);
assert!(
cut >= 0.80,
"minimal tier should cut >= 80% of advertised tokens vs full: \
full={full_tokens} minimal={minimal_tokens} cut={:.2}%",
cut * 100.0
);
eprintln!(
"TR-8 dev/01: full={full_tokens} tok minimal={minimal_tokens} tok cut={:.1}%",
cut * 100.0
);
}
fn assert_structurally_valid_schema(v: &serde_json::Value) {
let Some(obj) = v.as_object() else { return };
if let Some(props) = obj.get("properties").and_then(|p| p.as_object()) {
if let Some(req) = obj.get("required").and_then(|r| r.as_array()) {
for r in req {
let name = r.as_str().expect("required entries must be strings");
assert!(
props.contains_key(name),
"required `{name}` must name an existing property"
);
}
}
for (_, prop) in props {
if let Some(t) = prop.get("type") {
assert!(t.is_string(), "property `type` must be a string: {prop}");
}
if let Some(items) = prop.get("items") {
assert_structurally_valid_schema(items);
}
if prop.get("properties").is_some() {
assert_structurally_valid_schema(prop);
}
}
}
}
#[tokio::test]
async fn dev02_schemas_stay_valid_and_preserve_required_and_types_exactly() {
let full = advertised_tools(SchemaTier::Full).await;
let medium = advertised_tools(SchemaTier::Medium).await;
let minimal = advertised_tools(SchemaTier::Minimal).await;
for tier_name_tools in [("medium", &medium), ("minimal", &minimal)] {
let (label, tools) = tier_name_tools;
for t in tools {
assert_structurally_valid_schema(&t.parameters);
let full_match = full.iter().find(|f| f.name == t.name).unwrap();
assert_eq!(
t.parameters.get("required"),
full_match.parameters.get("required"),
"[{label}] `required` must be byte-identical to full for {}",
t.name
);
let full_props = full_match.parameters["properties"].as_object().unwrap();
let tier_props = t.parameters["properties"].as_object().unwrap();
assert_eq!(
full_props.keys().collect::<std::collections::BTreeSet<_>>(),
tier_props.keys().collect::<std::collections::BTreeSet<_>>(),
"[{label}] property set must be unchanged for {}",
t.name
);
for (key, full_prop) in full_props {
let tier_prop = &tier_props[key];
assert_eq!(
full_prop.get("type"),
tier_prop.get("type"),
"[{label}] type of `{key}` on {} must be preserved exactly",
t.name
);
}
}
}
}
struct SearchThenAnswer {
calls: AtomicUsize,
query: String,
}
#[async_trait]
impl Provider for SearchThenAnswer {
async fn complete(
&self,
_req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode_harness::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
if n == 0 {
let call = ChatMessage {
role: Role::Assistant,
content: None,
content_parts: None,
tool_calls: Some(vec![ToolCall {
id: "s1".into(),
kind: "function".into(),
function: FunctionCall {
name: "tool_search".into(),
arguments: serde_json::json!({"query": self.query}).to_string(),
},
}]),
tool_call_id: None,
name: None,
metadata: Default::default(),
};
return Ok((call, Usage::default()));
}
Ok((ChatMessage::assistant("done"), Usage::default()))
}
}
#[tokio::test]
async fn dev03_tool_search_fetch_returns_full_schema_byte_equal_to_as_shipped() {
let target = FatTool::new(7);
let as_shipped_description = target.description().to_string();
let as_shipped_parameters = target.parameters();
let mut registry = ToolRegistry::new();
registry.register(FatTool::new(7));
let config = Config::builder()
.schema_tier(SchemaTier::Minimal) .tool_advertising(supercode_harness::ToolAdvertising::Deferred { core: vec![] })
.build();
let mut agent = Agent::with_parts(
config,
Box::new(SearchThenAnswer {
calls: AtomicUsize::new(0),
query: "fatserver__tool_7".to_string(),
}),
registry,
);
agent.send("find and describe the tool").await.unwrap();
let history = agent.history();
let search_call = history
.iter()
.find_map(|m| {
(m.role == Role::Assistant)
.then(|| {
m.tool_calls()
.iter()
.find(|c| c.function.name == "tool_search")
})
.flatten()
})
.expect("a tool_search call must be in history");
let result = history
.iter()
.find(|m| {
m.role == Role::Tool && m.tool_call_id.as_deref() == Some(search_call.id.as_str())
})
.expect("a matching tool result must be in history");
let parsed: serde_json::Value =
serde_json::from_str(result.content.as_deref().unwrap_or_default()).unwrap();
let arr = parsed.as_array().unwrap();
let fetched = arr
.iter()
.find(|v| v.get("name").and_then(|n| n.as_str()) == Some("mcp__fatserver__tool_7"))
.expect("the fetched schema must be present");
assert_eq!(
fetched.get("description").and_then(|d| d.as_str()),
Some(as_shipped_description.as_str()),
"fetched description must be byte-equal to as-shipped, not tier-minified"
);
assert_eq!(
fetched.get("parameters"),
Some(&as_shipped_parameters),
"fetched parameters must be byte-equal to as-shipped, not tier-minified"
);
}
struct FixedReply;
#[async_trait]
impl Provider for FixedReply {
async fn complete(
&self,
_req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode_harness::Result<(ChatMessage, Usage)> {
Ok((
ChatMessage::assistant("a fixed, tier-independent reply"),
Usage::default(),
))
}
}
fn temp_dir(tag: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!(
"supercode-schema-tiers-{tag}-{}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
#[tokio::test]
async fn dev04_export_purity_sessions_under_any_tier_export_identically() {
async fn run_under(tier: SchemaTier, dir: &Path) -> (Vec<ChatMessage>, String) {
let config = Config::builder()
.cwd(dir.to_path_buf())
.schema_tier(tier)
.build();
let mut agent = Agent::with_parts(config, Box::new(FixedReply), build_fat_registry());
agent.send("hello").await.unwrap();
let transcript_path = dir.join(format!("transcript-{}.jsonl", tier.as_str()));
agent.save_transcript(&transcript_path).unwrap();
let transcript = std::fs::read_to_string(&transcript_path).unwrap();
(agent.history().to_vec(), transcript)
}
let dir = temp_dir("purity");
let (full_history, full_transcript) = run_under(SchemaTier::Full, &dir).await;
let (minimal_history, minimal_transcript) = run_under(SchemaTier::Minimal, &dir).await;
let full_history_json = serde_json::to_string(&full_history).unwrap();
let minimal_history_json = serde_json::to_string(&minimal_history).unwrap();
assert_eq!(
full_history_json, minimal_history_json,
"session content (history) must be byte-identical across tiers — \
tool schemas are config, never session content"
);
assert_eq!(
full_transcript, minimal_transcript,
"exported transcript must be byte-identical across tiers"
);
for needle in ["SchemaTier", "schema_tier", "\"minimal\"", "\"medium\""] {
assert!(
!minimal_transcript.contains(needle),
"exported transcript must carry no schema-tier trace (`{needle}`):\n{minimal_transcript}"
);
}
std::fs::remove_dir_all(&dir).ok();
}
#[tokio::test]
async fn dev05a_same_registry_and_tier_is_byte_identical_across_runs() {
let run1 = advertised_tools(SchemaTier::Medium).await;
let run2 = advertised_tools(SchemaTier::Medium).await;
assert_eq!(
serde_json::to_string(&run1).unwrap(),
serde_json::to_string(&run2).unwrap(),
"same registry + same tier must produce a byte-identical advertised set"
);
}
struct PlainAnswerCapturing {
calls: AtomicUsize,
requests: Arc<Mutex<Vec<Vec<ChatMessage>>>>,
}
#[async_trait]
impl Provider for PlainAnswerCapturing {
async fn complete(
&self,
req: &ChatRequest,
_on_delta: &(dyn for<'a> Fn(&'a str) + Send + Sync),
) -> supercode_harness::Result<(ChatMessage, Usage)> {
let n = self.calls.fetch_add(1, Ordering::SeqCst);
self.requests.lock().unwrap().push(req.messages.clone());
Ok((
ChatMessage::assistant(format!("reply {n}")),
Usage::default(),
))
}
}
#[tokio::test]
async fn dev05b_mid_session_tier_change_is_flagged_as_a_cache_bust() {
let session = load_codex();
let requests = Arc::new(Mutex::new(Vec::new()));
let config = Config::builder()
.cache_plan(CachePlan::ImportedPrefix)
.build();
let mut agent = Agent::with_provider(
config,
Box::new(PlainAnswerCapturing {
calls: AtomicUsize::new(0),
requests: requests.clone(),
}),
);
agent.load_session(session);
agent.send("turn one").await.unwrap();
agent.set_schema_tier(SchemaTier::Minimal);
agent.send("turn two").await.unwrap();
agent.send("turn three").await.unwrap();
let reqs = requests.lock().unwrap().clone();
assert_eq!(reqs.len(), 3);
let has_cache_control = |msgs: &[ChatMessage]| {
serde_json::to_string(msgs)
.unwrap()
.contains("cache_control")
};
assert!(
has_cache_control(&reqs[0]),
"turn 1 should be cache-annotated"
);
assert!(
!has_cache_control(&reqs[1]),
"turn 2 (right after the tier change) must be flagged as a cache bust \
— no cache_control annotation on the request that would have claimed \
a stale-cache-key hit"
);
assert!(
has_cache_control(&reqs[2]),
"turn 3 (tier unchanged since turn 2) should resume normal cache annotation"
);
}