use std::path::Path;
use std::process::Command;
use tempfile::TempDir;
fn aristo_bin() -> &'static str {
env!("CARGO_BIN_EXE_aristo")
}
fn aristo_in(workspace: &Path) -> Command {
let mut c = Command::new(aristo_bin());
c.env_clear();
if let Ok(path) = std::env::var("PATH") {
c.env("PATH", path);
}
#[cfg(target_os = "macos")]
if let Ok(p) = std::env::var("DYLD_FALLBACK_LIBRARY_PATH") {
c.env("DYLD_FALLBACK_LIBRARY_PATH", p);
}
let home = workspace.join("home");
std::fs::create_dir_all(&home).unwrap();
c.env("HOME", &home);
c.env("XDG_CONFIG_HOME", home.join("xdg"));
c.current_dir(workspace);
c
}
fn setup_workspace(source_body: &str) -> TempDir {
let tmp = TempDir::new().unwrap();
std::fs::write(tmp.path().join("aristo.toml"), "").unwrap();
std::fs::create_dir_all(tmp.path().join("src")).unwrap();
std::fs::create_dir_all(tmp.path().join(".aristo")).unwrap();
std::fs::write(tmp.path().join("src").join("lib.rs"), source_body).unwrap();
std::fs::write(
tmp.path().join("Cargo.toml"),
"[package]\nname = \"sandbox\"\nversion = \"0.0.1\"\nedition = \"2021\"\n",
)
.unwrap();
tmp
}
fn write_match_fixture(fixture_dir: &Path) {
std::fs::create_dir_all(fixture_dir).unwrap();
let body = r#"
effective_scopes = ["turso", ":vanilla"]
canon_version = "v0.2.0"
matched_at = "2026-06-05T00:00:00Z"
results = [
[
{ canon_id = "wal_commit_requires_fsync", version = "v0.1.0", canonical_text = "a commit frame must reach stable storage via fsync", confidence = 0.91, scope = "turso", prefix_tier = "aristos:", backed_by = "golden model + proofs + differential testing", linked = "arta_5b1c9f2a7e3d4068", verification = { coverage_level = "tight", test_binaries = ["wal_conform"] } }
]
]
[[suggestions]]
for_canon_id = "wal_commit_requires_fsync"
objective = { canon_id = "wal_protocol_correctness", version = "v0.1.0", canonical_text = "the WAL subsystem maintains LSN monotonicity and frame commitment ordering", scope = "turso", prefix_tier = "kanon:", backed_by = "", relationship = "parent" }
siblings = [
{ canon_id = "wal_find_frame_range_invariant", version = "v0.1.0", canonical_text = "find_frame never reads outside the live frame range", scope = "turso", prefix_tier = "aristos:", backed_by = "golden model + proofs + differential testing", relationship = "sibling" },
{ canon_id = "wal_checkpoint_error_no_db_leak", version = "v0.1.0", canonical_text = "a checkpoint failure must not leak frames into the main database file", scope = "turso", prefix_tier = "aristos:", backed_by = "golden model + proofs + differential testing", relationship = "sibling" }
]
"#;
std::fs::write(fixture_dir.join("match.toml"), body).unwrap();
}
const SOURCE: &str = r#"
#[aristo::intent(
"a commit frame must reach stable storage via fsync before durable",
id = "wal_commit_durable_invariant"
)]
pub fn commit() {}
"#;
fn stamped_workspace() -> TempDir {
let ws = setup_workspace(SOURCE);
let fixture_dir = ws.path().join("fixture");
write_match_fixture(&fixture_dir);
let out = aristo_in(ws.path())
.args(["stamp"])
.env("ARISTO_CANON_FIXTURE", &fixture_dir)
.output()
.unwrap();
assert!(
out.status.success(),
"stamp failed: {}\n{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
ws
}
#[test]
fn suggestions_list_with_empty_queue_reports_none() {
let ws = setup_workspace(SOURCE);
aristo_in(ws.path())
.args(["stamp", "--skip-canon"])
.status()
.unwrap();
let out = aristo_in(ws.path())
.args(["canon", "suggestions"])
.output()
.unwrap();
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("no proof-tree suggestions"),
"got: {stdout}"
);
}
#[test]
fn stamp_populates_queue_and_list_shows_the_cluster() {
let ws = stamped_workspace();
let out = aristo_in(ws.path())
.args(["canon", "suggestions"])
.output()
.unwrap();
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("proof-tree suggestions (1 cluster(s))"),
"got: {stdout}"
);
assert!(stdout.contains("wal_protocol_correctness"), "got: {stdout}");
assert!(stdout.contains("2 sibling(s)"), "got: {stdout}");
assert!(
stdout.contains("for wal_commit_requires_fsync"),
"got: {stdout}"
);
}
#[test]
fn suggestions_show_renders_objective_and_siblings() {
let ws = stamped_workspace();
let out = aristo_in(ws.path())
.args(["canon", "suggestions", "wal_protocol_correctness"])
.output()
.unwrap();
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("objective: wal_protocol_correctness"),
"got: {stdout}"
);
assert!(stdout.contains("kanon: tier"), "got: {stdout}");
assert!(
stdout.contains("wal_find_frame_range_invariant"),
"got: {stdout}"
);
assert!(
stdout.contains("wal_checkpoint_error_no_db_leak"),
"got: {stdout}"
);
assert!(
stdout.contains("wal_commit_requires_fsync"),
"got: {stdout}"
);
}
#[test]
fn suggestions_show_unknown_cluster_errors() {
let ws = stamped_workspace();
let out = aristo_in(ws.path())
.args(["canon", "suggestions", "does_not_exist"])
.output()
.unwrap();
assert!(!out.status.success());
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("no queued suggestion cluster"),
"got: {stderr}"
);
}
#[test]
fn suggestions_counts_emits_machine_readable_json() {
let ws = stamped_workspace();
let out = aristo_in(ws.path())
.args(["canon", "suggestions", "--counts"])
.output()
.unwrap();
assert!(out.status.success());
let stdout = String::from_utf8_lossy(&out.stdout);
let v: serde_json::Value =
serde_json::from_str(stdout.trim()).unwrap_or_else(|e| panic!("not JSON: {e}\n{stdout}"));
assert!(v.get("matches").is_some(), "got: {v}");
assert!(v.get("suggestions").is_some(), "got: {v}");
assert!(v["matches"].get("new").is_some(), "got: {v}");
assert!(v["matches"].get("pending").is_some(), "got: {v}");
assert!(v["suggestions"].get("new").is_some(), "got: {v}");
assert!(v["suggestions"].get("pending").is_some(), "got: {v}");
assert_eq!(v["suggestions"]["new"], 1, "got: {v}");
}
#[test]
fn dedup_two_drops_member_already_pending_in_cache() {
let ws = setup_workspace(
r#"
#[aristo::intent(
"a commit frame must reach stable storage via fsync before durable",
id = "wal_commit_durable_invariant"
)]
pub fn commit() {}
#[aristo::intent(
"find_frame never reads outside the live frame range",
id = "wal_find_frame_invariant"
)]
pub fn find_frame() {}
"#,
);
let fixture_dir = ws.path().join("fixture");
std::fs::create_dir_all(&fixture_dir).unwrap();
let body = r#"
effective_scopes = ["turso", ":vanilla"]
canon_version = "v0.2.0"
matched_at = "2026-06-05T00:00:00Z"
results = [
[
{ canon_id = "wal_commit_requires_fsync", version = "v0.1.0", canonical_text = "a commit frame must reach stable storage via fsync", confidence = 0.91, scope = "turso", prefix_tier = "aristos:", backed_by = "golden model", linked = "arta_5b1c9f2a7e3d4068", verification = { coverage_level = "tight", test_binaries = [] } }
],
[
{ canon_id = "wal_find_frame_range_invariant", version = "v0.1.0", canonical_text = "find_frame never reads outside the live frame range", confidence = 0.88, scope = "turso", prefix_tier = "aristos:", backed_by = "golden model", linked = "arta_aaaa1111bbbb2222", verification = { coverage_level = "tight", test_binaries = [] } }
]
]
[[suggestions]]
for_canon_id = "wal_commit_requires_fsync"
objective = { canon_id = "wal_protocol_correctness", version = "v0.1.0", canonical_text = "the WAL subsystem maintains correctness", scope = "turso", prefix_tier = "kanon:", backed_by = "", relationship = "parent" }
siblings = [
{ canon_id = "wal_find_frame_range_invariant", version = "v0.1.0", canonical_text = "find_frame never reads outside the live frame range", scope = "turso", prefix_tier = "aristos:", backed_by = "golden model", relationship = "sibling" },
{ canon_id = "wal_checkpoint_error_no_db_leak", version = "v0.1.0", canonical_text = "a checkpoint failure must not leak frames", scope = "turso", prefix_tier = "aristos:", backed_by = "golden model", relationship = "sibling" }
]
"#;
std::fs::write(fixture_dir.join("match.toml"), body).unwrap();
let out = aristo_in(ws.path())
.args(["stamp"])
.env("ARISTO_CANON_FIXTURE", &fixture_dir)
.output()
.unwrap();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
let show = aristo_in(ws.path())
.args(["canon", "suggestions", "wal_protocol_correctness"])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&show.stdout);
assert!(
!stdout.contains("wal_find_frame_range_invariant"),
"dedup ② should drop the sibling that is a primary match; got: {stdout}"
);
assert!(
stdout.contains("wal_checkpoint_error_no_db_leak"),
"got: {stdout}"
);
}