use crate::brain::rsi_stale_scan::{
ALL_ANCHOR_KINDS, AnchorKind, FindingAction, LineClass, Verdict, anchor_kind, backtick_spans,
classify_line, decide_action, provider_mentions, scan_brain_files, verify_binary,
verify_config_key, verify_path, verify_provider,
};
use crate::config::{Config, ProviderConfig};
#[test]
fn historical_dated_lesson_is_exempt() {
let line = "- `cmd-wrap` removed Aug 10 — use plain bash now";
assert_eq!(classify_line(line), LineClass::HistoricalExempt);
}
#[test]
fn exemption_class_serializes_as_historical_exempt() {
assert_eq!(
serde_json::to_string(&LineClass::HistoricalExempt).unwrap(),
"\"historical_exempt\""
);
assert_eq!(
serde_json::to_string(&LineClass::Prescription).unwrap(),
"\"prescription\""
);
}
#[test]
fn violation_ledger_is_exempt() {
let line = "Violations: 3, last 2026-08-22 (session fd72101f)";
assert_eq!(classify_line(line), LineClass::HistoricalExempt);
}
#[test]
fn ledger_wording_without_date_is_exempt() {
let line = "- Violation ledger: hit p95 twice (session f00d)";
assert_eq!(classify_line(line), LineClass::HistoricalExempt);
}
#[test]
fn imperative_mood_with_non_dated_refs_is_prescription_dated_counterpart_is_exempt() {
let imperative = "- never use `cmd-wrap` in new sessions";
assert_eq!(classify_line(imperative), LineClass::Prescription);
let dated = "- never use `cmd-wrap` in new sessions (rule broken 2026-06-01)";
assert_eq!(classify_line(dated), LineClass::HistoricalExempt);
let prose = "The quick brown fox jumps over the lazy dog";
assert_eq!(classify_line(prose), LineClass::Neutral);
}
#[test]
fn bare_year_marks_historical() {
assert!(crate::brain::rsi_stale_scan::has_date_marker(
"shipped in 2026"
));
assert!(!crate::brain::rsi_stale_scan::has_date_marker(
"no temporal marker here"
));
assert!(!crate::brain::rsi_stale_scan::has_date_marker(
"the augment script"
));
assert!(!crate::brain::rsi_stale_scan::has_date_marker(
"decimate the old rows"
));
assert!(!crate::brain::rsi_stale_scan::has_date_marker(
"junior dev notes"
));
assert!(crate::brain::rsi_stale_scan::has_date_marker(
"removed on aug 12"
));
assert!(crate::brain::rsi_stale_scan::has_date_marker(
"2026-08-26: ledger entry"
));
assert!(!crate::brain::rsi_stale_scan::has_date_marker(
"session fd72101f"
));
}
#[test]
fn imperative_rule_is_prescription() {
let line = "- ALWAYS run `cargo clippy --all-features` before commit";
assert_eq!(classify_line(line), LineClass::Prescription);
}
#[test]
fn dead_binary_in_prescription_verifies_stale() {
let line = "- run `definitely-not-a-real-binary-9x2` before commit";
assert_eq!(classify_line(line), LineClass::Prescription);
assert_eq!(
anchor_kind("definitely-not-a-real-binary-9x2", line),
Some(AnchorKind::Binary)
);
assert_eq!(
verify_binary("definitely-not-a-real-binary-9x2"),
Verdict::Stale
);
}
#[test]
fn binary_span_with_argv_verifies_first_token() {
assert_eq!(verify_binary("sh -c 'echo hi'"), Verdict::Ok);
assert_eq!(verify_binary("cargo clippy --all-features"), Verdict::Ok);
}
#[test]
fn shell_builtin_verifies_ok() {
for b in ["cd", "source", "export", "true"] {
assert_eq!(verify_binary(b), Verdict::Ok, "builtin `{b}` must be Ok");
}
}
#[test]
fn stale_findings_split_into_exactly_two_actions() {
for kind in [
AnchorKind::Binary,
AnchorKind::ConfigKey,
AnchorKind::ProviderName,
] {
assert_eq!(
decide_action(kind, Verdict::Stale),
FindingAction::RewordViaUpdate,
"stale {kind:?} must be rewordable via self_improve update"
);
}
assert_eq!(
decide_action(AnchorKind::FilePath, Verdict::Stale),
FindingAction::SurfaceToUser,
"vanished path is a removal candidate: never executed autonomously"
);
for kind in ALL_ANCHOR_KINDS {
assert_eq!(decide_action(kind, Verdict::Ok), FindingAction::None);
assert_eq!(
decide_action(kind, Verdict::Unverifiable),
FindingAction::None
);
}
}
#[test]
fn finding_action_slugs_pin_no_delete() {
assert_eq!(
serde_json::to_string(&FindingAction::RewordViaUpdate).unwrap(),
"\"reword_via_update\""
);
assert_eq!(
serde_json::to_string(&FindingAction::SurfaceToUser).unwrap(),
"\"surface_to_user\""
);
assert_eq!(
serde_json::to_string(&FindingAction::None).unwrap(),
"\"none\""
);
for (a, slug) in [
(FindingAction::RewordViaUpdate, "reword_via_update"),
(FindingAction::SurfaceToUser, "surface_to_user"),
(FindingAction::None, "none"),
] {
assert_eq!(a.as_str(), slug);
assert!(!slug.contains("delete") && !slug.contains("remove") && !slug.contains("prune"));
}
for kind in ALL_ANCHOR_KINDS {
for verdict in [Verdict::Ok, Verdict::Stale, Verdict::Unverifiable] {
let action = decide_action(kind, verdict);
let slug = serde_json::to_string(&action).unwrap();
assert!(
!slug.contains("delete") && !slug.contains("remove") && !slug.contains("prune"),
"action `{slug}` must never be a removal"
);
}
}
}
#[test]
fn slash_command_is_not_world_state() {
assert_eq!(anchor_kind("/help", "use `/help` when lost"), None);
}
#[test]
fn react_directive_is_skipped() {
assert_eq!(anchor_kind("<<react:thumbsup>>", "emit <<react:👍>>"), None);
}
#[test]
fn snake_case_function_name_without_command_context_is_skipped() {
let line = "the helper `scan_after_brain_write` runs post-write";
assert_eq!(anchor_kind("scan_after_brain_write", line), None);
}
#[test]
fn file_path_missing_flags_with_surface_action() {
let line = "- read `/definitely/missing/path-xyz.md` first";
assert_eq!(
anchor_kind("/definitely/missing/path-xyz.md", line),
Some(AnchorKind::FilePath)
);
assert_eq!(
decide_action(AnchorKind::FilePath, Verdict::Stale),
FindingAction::SurfaceToUser
);
}
#[test]
fn path_verdicts_are_typed_and_conservative() {
assert_eq!(verify_path("*.md"), Verdict::Unverifiable);
assert_eq!(
verify_path("no/such/relative-xyz.md"),
Verdict::Unverifiable
);
assert_eq!(
verify_path("/definitely/no/such/file-9x.md"),
Verdict::Stale
);
let tmp = std::env::temp_dir().join(format!("rsi_stale_scan_ok_{}", std::process::id()));
std::fs::write(&tmp, "x").unwrap();
assert_eq!(verify_path(tmp.to_str().unwrap()), Verdict::Ok);
let _ = std::fs::remove_file(&tmp);
assert_eq!(
verify_path("~/definitely/no/such/thing-9x.md"),
Verdict::Stale
);
}
#[test]
fn config_keys_verify_against_embedded_schema() {
assert_eq!(verify_config_key("[agent]"), Verdict::Ok);
assert_eq!(verify_config_key("[agent.approval_policy]"), Verdict::Ok);
assert_eq!(verify_config_key("[database.path]"), Verdict::Ok);
assert_eq!(verify_config_key("[logging.level]"), Verdict::Ok);
assert_eq!(verify_config_key("[doctor]"), Verdict::Ok);
assert_eq!(verify_config_key("[gateway]"), Verdict::Ok);
assert_eq!(verify_config_key("[memory.embedding.url]"), Verdict::Ok);
assert_eq!(
verify_config_key("[providers.anthropic.api_key]"),
Verdict::Ok
);
assert_eq!(
verify_config_key("[providers.claude_cli.default_model]"),
Verdict::Ok
);
assert_eq!(
verify_config_key("[providers.fallback.enabled]"),
Verdict::Ok
);
assert_eq!(
verify_config_key("[providers.web_search.brave.api_key]"),
Verdict::Ok
);
assert_eq!(verify_config_key("[bogus_section_nope]"), Verdict::Stale);
assert_eq!(verify_config_key("[agent.bogus_leaf]"), Verdict::Stale);
assert_eq!(verify_config_key("[memory.bogus.leaf]"), Verdict::Stale);
assert_eq!(
verify_config_key("[channels.telegram.groups.mygroup.name]"),
Verdict::Unverifiable
);
assert_eq!(
verify_config_key("[providers.custom.myrepo.base_url]"),
Verdict::Unverifiable
);
assert_eq!(
verify_config_key("[database.path.deeper]"),
Verdict::Unverifiable
);
}
#[test]
fn schema_derived_sections_are_the_registry() {
assert!(
crate::config::sections::ignored_key_paths("[doctor]\nauto_fix = true\n")
.unwrap()
.is_empty(),
"doctor is a real Config section — the struct must not ignore it"
);
assert_eq!(
crate::config::sections::ignored_key_paths("[doctorr]\nauto_fix = true\n").unwrap(),
vec!["doctorr".to_string()],
"an unknown section is reported at its head"
);
let known = crate::config::sections::known_sections();
for real in [
"agent",
"daemon",
"a2a",
"image",
"cron",
"memory",
"brain",
"browser",
"doctor",
"tui",
"channels",
"provider_registry",
"database",
"logging",
"debug",
"providers",
] {
assert!(
known.iter().any(|s| s == real),
"known_sections() must include {real}"
);
}
assert!(!known.iter().any(|s| s == "voice"));
assert!(!known.iter().any(|s| s == "gateway"));
}
fn cfg_with(provider: &str) -> Config {
let mut config = Config::default();
let pc = Some(ProviderConfig {
api_key: Some("sk-test".into()),
..Default::default()
});
let p = &mut config.providers;
match provider {
"anthropic" => p.anthropic = pc,
"zai" => p.zai = pc,
"github" => p.github = pc,
_ => panic!("unknown provider in test helper"),
}
config
}
#[test]
fn provider_verification_uses_configured_table() {
let config = cfg_with("anthropic");
assert_eq!(verify_provider("anthropic", &config), Verdict::Ok);
assert_eq!(verify_provider("kimi", &cfg_with("zai")), Verdict::Stale);
assert_eq!(verify_provider("tencent/Hy3", &config), Verdict::Stale);
assert_eq!(verify_provider("openai", &config), Verdict::Stale);
assert_eq!(verify_provider("claude-cli", &config), Verdict::Ok);
assert_eq!(verify_provider("", &config), Verdict::Unverifiable);
}
#[test]
fn retired_providers_flag_stale() {
let config = Config::default();
for dead in ["tencent/Hy3", "cmd-wrap", "omdc-proxy"] {
assert_eq!(
verify_provider(dead, &config),
Verdict::Stale,
"`{dead}` is retired and must verify stale"
);
}
}
#[test]
fn provider_mention_extraction_requires_cue_word() {
let hits = provider_mentions("provider zhipu works; providers github too");
assert_eq!(hits, vec!["github".to_string(), "zhipu".to_string()]);
assert!(provider_mentions("the word banana does not count").is_empty());
}
#[test]
fn backtick_spans_preserve_order() {
let spans = backtick_spans("use `foo` then `bar baz`");
assert_eq!(spans, vec!["foo".to_string(), "bar baz".to_string()]);
}
fn scan_dir(
lines: &str,
) -> (
std::path::PathBuf,
Vec<crate::brain::rsi_stale_scan::StaleFinding>,
) {
static SEQ: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
let n = SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let dir = std::env::temp_dir().join(format!("rsi_stale_scan_e2e_{}_{}", std::process::id(), n));
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("MEMORY.md"), lines).unwrap();
let findings = scan_brain_files(&Config::default(), &dir);
(dir, findings)
}
#[test]
fn scan_exempts_dated_lessons_but_splits_stale_prescriptions() {
let (dir, findings) = scan_dir(concat!(
"- run `cmd-wrap` (removed Aug 10) only when migrating old sessions\n",
"- ALWAYS run `omdc-proxy-is-dead-7f3` before send\n",
"- read `/no/such/dir-abc123.md` first\n",
));
let _ = std::fs::remove_dir_all(&dir);
assert!(
!findings.iter().any(|f| f.anchor == "cmd-wrap"),
"dated lesson must be exempt, got: {findings:?}"
);
assert_eq!(findings.len(), 2, "unexpected findings: {findings:?}");
let reword: Vec<_> = findings
.iter()
.filter(|f| f.action == FindingAction::RewordViaUpdate)
.collect();
let surface: Vec<_> = findings
.iter()
.filter(|f| f.action == FindingAction::SurfaceToUser)
.collect();
assert_eq!(
reword.len(),
1,
"dead binary → reword_via_update: {findings:?}"
);
assert_eq!(reword[0].anchor, "omdc-proxy-is-dead-7f3");
assert_eq!(
surface.len(),
1,
"vanished path → surface_to_user: {findings:?}"
);
assert_eq!(surface[0].anchor, "/no/such/dir-abc123.md");
assert!(reword[0].unique_key().starts_with("MEMORY.md:2:binary:"));
assert!(
surface[0]
.unique_key()
.starts_with("MEMORY.md:3:file_path:")
);
}
#[test]
fn scan_records_ok_and_unverifiable_with_no_action() {
let (dir, findings) = scan_dir(concat!(
"- run `sh -c 'echo hi'` to smoke the shell\n",
"- read `*.md` files first\n",
"- set `[agent.approval_policy]` to auto-session\n",
));
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(findings.len(), 3, "unexpected findings: {findings:?}");
for f in &findings {
assert_eq!(f.action, FindingAction::None, "nothing to amend: {f:?}");
assert_ne!(f.verdict, Verdict::Stale);
}
let verdicts: Vec<(String, Verdict)> = findings
.iter()
.map(|f| (f.anchor.clone(), f.verdict))
.collect();
assert!(verdicts.contains(&("sh -c 'echo hi'".to_string(), Verdict::Ok)));
assert!(verdicts.contains(&("*.md".to_string(), Verdict::Unverifiable)));
assert!(verdicts.contains(&("[agent.approval_policy]".to_string(), Verdict::Ok)));
}
#[test]
fn scan_flags_unconfigured_provider_mention() {
let (dir, findings) = scan_dir("- route coding sessions to provider zhipu\n");
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(findings.len(), 1, "unexpected findings: {findings:?}");
assert_eq!(findings[0].anchor_kind, AnchorKind::ProviderName);
assert_eq!(findings[0].verdict, Verdict::Stale);
assert_eq!(findings[0].action, FindingAction::RewordViaUpdate);
}
#[test]
fn scan_treats_backticked_provider_name_as_provider() {
let (dir, findings) = scan_dir("- always use provider `anthropic` for vision work\n");
let _ = std::fs::remove_dir_all(&dir);
assert_eq!(findings.len(), 1, "unexpected findings: {findings:?}");
assert_eq!(findings[0].anchor_kind, AnchorKind::ProviderName);
assert_eq!(findings[0].verdict, Verdict::Stale); }