use std::path::PathBuf;
use std::process::Command;
struct Transcript {
dir: PathBuf,
lines: Vec<String>,
n: usize,
}
impl Transcript {
fn new(name: &str) -> Transcript {
let dir =
std::env::temp_dir().join(format!("amont-agent-mine-{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("projects/p")).expect("scratch dir");
std::fs::write(dir.join("global"), "").expect("global config");
std::fs::write(dir.join("system"), "").expect("system config");
Transcript {
dir,
lines: Vec::new(),
n: 0,
}
}
fn call(&mut self, model: &str, command: &str, failed: bool) -> &mut Self {
self.n += 1;
let id = format!("t{}", self.n);
let day = 3 + self.n / 40;
let stamp = format!("2026-08-{day:02}T10:00:00.000Z");
let command = escape(command);
self.lines.push(format!(
r#"{{"sessionId":"s","cwd":"/tmp","timestamp":"{stamp}","type":"assistant","message":{{"model":"{model}","content":[{{"type":"tool_use","id":"{id}","name":"Bash","input":{{"command":"{command}"}}}}]}}}}"#
));
self.lines.push(format!(
r#"{{"sessionId":"s","cwd":"/tmp","timestamp":"{stamp}","type":"user","message":{{"content":[{{"tool_use_id":"{id}","type":"tool_result","content":"out","is_error":{failed}}}]}}}}"#
));
self
}
fn other(&mut self) -> &mut Self {
self.n += 1;
let id = format!("t{}", self.n);
let day = 3 + self.n / 40;
self.lines.push(format!(
r#"{{"sessionId":"s","cwd":"/tmp","timestamp":"2026-08-{day:02}T10:00:00.000Z","type":"assistant","message":{{"model":"m","content":[{{"type":"tool_use","id":"{id}","name":"Read","input":{{"file_path":"/tmp/x"}}}}]}}}}"#
));
self
}
fn write(&self) -> &PathBuf {
let mut out = self.lines.join("\n");
out.push('\n');
std::fs::write(self.dir.join("projects/p/session.jsonl"), out).expect("transcript");
&self.dir
}
fn run(&self, args: &[&str]) -> String {
let dir = self.write();
let out = Command::new(env!("CARGO_BIN_EXE_amont-agent"))
.args(args)
.arg("--transcripts")
.arg(dir.join("projects"))
.env("GIT_CONFIG_GLOBAL", dir.join("global"))
.env("GIT_CONFIG_SYSTEM", dir.join("system"))
.env_remove("AMONT_AGENT_OFF")
.output()
.expect("the binary runs");
assert!(
out.status.success(),
"{args:?} exited {:?}: {}",
out.status.code(),
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).into_owned()
}
}
fn escape(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}
#[test]
fn a_repeated_mistake_is_proposed_with_its_samples() {
let mut t = Transcript::new("propose");
for i in 0..6 {
t.call("m", &format!("tofu apply -target module.a{i}"), true);
t.call("m", &format!("tofu apply -target module.b{i}"), false);
t.other().other().other().other();
}
let out = t.run(&["mine"]);
assert!(
out.contains("tofu apply"),
"the shape should be proposed:\n{out}"
);
assert!(
out.contains("-target"),
"the flag is part of the shape:\n{out}"
);
assert!(
out.contains("tofu apply -target module.a0"),
"a sample of a call that went wrong:\n{out}"
);
}
#[test]
fn a_polling_loop_is_not_a_run_of_mistakes() {
let mut t = Transcript::new("poll");
for i in 0..20 {
t.call("m", &format!("sleep 5 && curl -sS http://h/{i}"), false);
}
let out = t.run(&["mine"]);
assert!(
!out.contains("curl"),
"a loop is not twenty corrections:\n{out}"
);
assert!(out.contains("no shape clears the thresholds"), "{out}");
}
#[test]
fn a_shape_a_rule_already_names_is_listed_apart() {
let mut t = Transcript::new("covered");
for i in 0..6 {
t.call(
"m",
&format!("git push origin feat/a{i} 2>&1 | tail -5"),
true,
);
t.call("m", &format!("git push origin feat/b{i}"), false);
t.other().other().other().other();
}
let out = t.run(&["mine"]);
let (proposed, covered) = out
.split_once("already covered by a rule")
.expect("a covered section");
assert!(
covered.contains("pipe-to-tail"),
"named by the rule that covers it:\n{out}"
);
assert!(
!proposed.contains("tail -5"),
"it must not also be proposed:\n{out}"
);
}
#[test]
fn format_cases_emits_unreviewed_lines_in_the_corpus_format() {
let mut t = Transcript::new("cases");
for i in 0..6 {
t.call("m", &format!("tofu apply -target module.a{i}"), true);
t.call("m", &format!("tofu apply -target module.b{i}"), false);
t.other().other().other().other();
}
let out = t.run(&["mine", "--format", "cases"]);
assert!(out.starts_with("# amont-agent-cases-v1\n"), "{out}");
let cases: Vec<&str> = out.lines().skip(1).filter(|l| !l.is_empty()).collect();
assert!(!cases.is_empty(), "no cases emitted:\n{out}");
for line in &cases {
let (verdict, command) = line.split_once('\t').expect("a tab-separated case");
assert_eq!(verdict, "?", "every case starts unreviewed");
assert!(!command.contains('\n'), "one line per case");
}
}
#[test]
fn compliance_separates_the_model_that_listened_from_the_one_that_did_not() {
let mut t = Transcript::new("compliance");
for i in 0..4 {
t.call("listener", &format!("rg --include=*.rs alpha{i}"), false);
t.call("listener", &format!("rg --include='*.rs' alpha{i}"), false);
t.call("deaf", &format!("rg --include=*.ts beta{i}"), false);
t.call("deaf", &format!("rg --include=*.ts beta{i}b"), false);
}
let out = t.run(&["backtest", "--compliance", "--rule", "glob-in-flag-value"]);
let rows: Vec<&str> = out
.lines()
.filter(|l| l.contains("listener") || l.contains("deaf"))
.collect();
assert_eq!(rows.len(), 2, "one row per model:\n{out}");
let listener = rows.iter().find(|r| r.contains("listener")).unwrap();
let deaf = rows.iter().find(|r| r.contains("deaf")).unwrap();
assert!(listener.ends_with("100%"), "{listener}");
assert!(deaf.ends_with("0%"), "{deaf}");
}
#[test]
fn compliance_leaves_out_what_was_refused() {
let mut t = Transcript::new("deny");
for i in 0..4 {
t.call("m", &format!("git push origin feat/x{i} | tail -5"), false);
}
let out = t.run(&["backtest", "--compliance", "--rule", "pipe-to-tail"]);
assert!(
out.contains("no rule fired over these transcripts"),
"a denied rule has no compliance to report:\n{out}"
);
}
#[test]
fn novelty_puts_the_unlike_match_first() {
let mut t = Transcript::new("novelty");
for i in 0..8 {
t.call("m", &format!("rg --include=*.rs alpha{i}"), false);
}
t.call("m", "helm template chart --set=image.tag=v1.*", false);
let plain = t.run(&["explain", "glob-in-flag-value", "--sample", "1"]);
let novel = t.run(&[
"explain",
"glob-in-flag-value",
"--sample",
"1",
"--rank",
"novelty",
]);
assert!(
plain.contains("--include"),
"the first match, as met:\n{plain}"
);
assert!(
novel.contains("--set="),
"the match least like the rest comes first:\n{novel}"
);
}
#[test]
fn an_unknown_rank_is_refused() {
let dir = Transcript::new("rank").write().clone();
let out = Command::new(env!("CARGO_BIN_EXE_amont-agent"))
.args(["explain", "pipe-to-tail", "--rank", "wisdom"])
.arg("--transcripts")
.arg(dir.join("projects"))
.output()
.expect("the binary runs");
assert_eq!(out.status.code(), Some(2));
assert!(String::from_utf8_lossy(&out.stderr).contains("--rank takes `novelty`"));
}