use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn fixture_ir() -> serde_json::Value {
let path =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples/driftwood-agent/ir.golden.json");
serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap()
}
fn prepare() -> (tempfile::TempDir, PathBuf) {
let root = tempfile::tempdir().unwrap();
let ir_dir = root.path().join("agent");
let project = root.path().join("project");
fs::create_dir_all(project.join("knowledge/rules")).unwrap();
fs::create_dir_all(&ir_dir).unwrap();
fs::write(
project.join("knowledge/rules/rule.md"),
"# CLI_KNOWLEDGE_MARKER\n\nUse the canonical rule.",
)
.unwrap();
let mut ir = fixture_ir();
ir["context_binding"]["project"] = serde_json::json!("../project");
for node in ir["components"].as_array_mut().unwrap() {
node["context_binding"]["project"] = serde_json::json!("../project");
}
let ir_path = ir_dir.join("ir.json");
fs::write(&ir_path, serde_json::to_string_pretty(&ir).unwrap()).unwrap();
(root, ir_path)
}
fn dispatch(ir: &Path, out: &Path) -> Output {
Command::new(env!("CARGO_BIN_EXE_warble"))
.args(["dispatch", "--strong", "sonnet"])
.arg(ir)
.arg("--out")
.arg(out)
.output()
.expect("warble dispatch runs")
}
#[test]
fn cli_dispatch_embeds_no_project_knowledge_and_leaks_no_path() {
let (root, ir) = prepare();
let out = root.path().join("schema-only");
let schema = dispatch(&ir, &out);
assert!(
schema.status.success(),
"{}",
String::from_utf8_lossy(&schema.stderr)
);
let agent = fs::read_to_string(out.join(".claude/agents/answer_query.md")).unwrap();
assert!(!agent.contains("CLI_KNOWLEDGE_MARKER"));
assert!(!agent.contains(&root.path().display().to_string()));
let report: serde_json::Value =
serde_json::from_str(&fs::read_to_string(out.join("context-report.json")).unwrap())
.unwrap();
assert_eq!(report["mode"], "schema-only");
assert!(report["knowledge_fingerprint"].is_null());
assert_eq!(report["knowledge_chars"], 0);
}
#[test]
fn the_injection_mode_is_not_a_caller_choice_and_the_flag_is_gone() {
let (root, ir) = prepare();
let out = root.path().join("retired");
let result = Command::new(env!("CARGO_BIN_EXE_warble"))
.args(["dispatch", "--context-injection", "schema+knowledge"])
.arg(&ir)
.arg("--out")
.arg(&out)
.output()
.expect("warble dispatch runs");
assert_eq!(result.status.code(), Some(2));
assert!(String::from_utf8_lossy(&result.stderr)
.contains("unexpected argument '--context-injection'"));
assert!(!out.exists());
}
#[test]
fn an_unknown_enum_knob_loud_fails_before_writing() {
let (root, ir) = prepare();
let out = root.path().join("unknown");
let result = Command::new(env!("CARGO_BIN_EXE_warble"))
.args(["dispatch", "--render-flavor", "guess"])
.arg(&ir)
.arg("--out")
.arg(&out)
.output()
.expect("warble dispatch runs");
assert_eq!(result.status.code(), Some(1));
assert!(String::from_utf8_lossy(&result.stderr)
.contains("unknown --render-flavor 'guess' (expected: programmatic, prompt)"));
assert!(!out.exists());
}
#[test]
fn an_unknown_enum_knob_loud_fails_on_every_early_returning_target() {
let knobs = [
(
"--render-flavor",
"unknown --render-flavor 'guess' (expected: programmatic, prompt)",
),
(
"--hybrid-realization",
"unknown --hybrid-realization 'guess' (expected: bash-script, mcp-server)",
),
];
for target in ["vercel", "codex:interactive"] {
for (knob, expected) in knobs {
let (root, ir) = prepare();
let out = root.path().join("early-return-unknown");
let result = Command::new(env!("CARGO_BIN_EXE_warble"))
.args(["dispatch", "--target", target, knob, "guess"])
.arg(&ir)
.arg("--out")
.arg(&out)
.output()
.expect("warble dispatch runs");
let case = format!("{target} {knob}");
assert_eq!(result.status.code(), Some(1), "{case}");
assert!(
String::from_utf8_lossy(&result.stderr).contains(expected),
"{case}"
);
assert!(!out.exists(), "{case}");
}
}
}