use std::collections::BTreeSet;
use std::path::PathBuf;
const SPEC: &str = "docs/architecture/SPEC-PR-Gate-Outputs-v1.md";
fn workspace_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..")
}
fn read(rel: &str) -> String {
let path = workspace_root().join(rel);
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()))
}
fn emittable_codes() -> BTreeSet<String> {
let src = read("crates/assay-cli/src/exit_codes.rs");
let body = after(&src, "pub fn as_str(&self) -> &'static str {");
let body = before(body, "\n }");
let mut found = BTreeSet::new();
for line in body.lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("ReasonCode::") else {
continue;
};
let Some((_variant, value)) = rest.split_once("=>") else {
continue;
};
let value = string_literal(value)
.unwrap_or_else(|| panic!("`as_str` arm is not a string literal: {line}"));
if value.is_empty() {
continue;
}
assert!(
found.insert(value.clone()),
"duplicate as_str value {value:?}"
);
}
assert!(
found.len() > 10,
"parsed only {} codes; the `as_str` shape moved",
found.len()
);
found
}
fn registered(section: &str) -> BTreeSet<String> {
let spec = read(SPEC);
let body = after(&spec, &format!("### {section}"));
let body = before(body, "\n### ");
body.lines()
.filter_map(|line| line.strip_prefix('|'))
.filter_map(|line| line.split('|').next())
.flat_map(|cell| cell.split(','))
.map(str::trim)
.filter(|token| is_code_shaped(token))
.map(str::to_owned)
.collect()
}
fn is_code_shaped(token: &str) -> bool {
!token.is_empty()
&& token.starts_with(|c: char| c.is_ascii_uppercase())
&& token
.chars()
.all(|c| c.is_ascii_uppercase() || c == '_' || c.is_ascii_digit())
}
fn after<'a>(haystack: &'a str, needle: &str) -> &'a str {
let idx = haystack
.find(needle)
.unwrap_or_else(|| panic!("not found: {needle}"));
&haystack[idx + needle.len()..]
}
fn before<'a>(haystack: &'a str, needle: &str) -> &'a str {
haystack
.split_once(needle)
.map(|(a, _)| a)
.unwrap_or(haystack)
}
fn string_literal(s: &str) -> Option<String> {
let start = s.find('"')? + 1;
let end = s[start..].find('"')? + start;
Some(s[start..end].to_owned())
}
fn remediation_matched_codes() -> BTreeSet<String> {
let src = read("crates/assay-core/src/agentic/builder.rs");
let mut found = BTreeSet::new();
for line in src.lines() {
let trimmed = line.trim();
if !trimmed.ends_with("=> {") || !trimmed.starts_with('"') {
continue;
}
for part in trimmed.trim_end_matches("=> {").split('|') {
if let Some(code) = string_literal(part) {
found.insert(code);
}
}
}
assert!(
!found.is_empty(),
"parsed no remediation match arms; the builder's shape moved"
);
found
}
fn all_registered() -> BTreeSet<String> {
["5.1", "5.2", "5.3", "5.4", "5.5"]
.iter()
.flat_map(|s| registered(s))
.collect()
}
#[test]
fn every_emittable_code_is_registered() {
let unregistered: Vec<String> = emittable_codes()
.difference(&all_registered())
.cloned()
.collect();
assert!(
unregistered.is_empty(),
"`ReasonCode::as_str` can emit these, and {SPEC} §5 registers none of them: {unregistered:?}. \
§5's normative rule requires reason_code to be a registered value, so an unregistered \
emittable code makes the CLI violate its own spec."
);
}
#[test]
fn every_registered_code_is_emittable_or_reserved() {
let emittable = emittable_codes();
let reserved = registered("5.4");
let policy_engine = registered("5.5");
let orphans: Vec<String> = ["5.1", "5.2", "5.3"]
.iter()
.flat_map(|s| registered(s))
.filter(|code| {
!emittable.contains(code) && !reserved.contains(code) && !policy_engine.contains(code)
})
.collect();
assert!(
orphans.is_empty(),
"{SPEC} registers these in an exit-code table, but nothing emits them and §5.4 does not \
list them as reserved: {orphans:?}. Either wire the code up, or record it as reserved — \
§175 forbids deleting it outright."
);
}
#[test]
fn every_remediation_branch_keys_on_a_registered_code() {
let known = all_registered();
let diagnostic_codes: BTreeSet<String> = {
let src = read("crates/assay-core/src/errors/diagnostic.rs");
let body = before(after(&src, "pub mod codes {"), "\n}");
body.lines()
.filter(|l| l.trim().starts_with("pub const "))
.filter_map(|l| l.split_once('=').and_then(|(_, v)| string_literal(v)))
.collect()
};
let unknown: Vec<String> = remediation_matched_codes()
.into_iter()
.filter(|code| !known.contains(code) && !diagnostic_codes.contains(code))
.collect();
assert!(
unknown.is_empty(),
"`agentic::builder` picks a remediation for these codes, and no registry knows them: \
{unknown:?}. A branch keyed on a code nothing emits and nothing registers cannot fire and \
cannot be reviewed — register it (§5.4 reserved is enough) or delete the arm."
);
}
#[test]
fn the_reserved_section_claims_nothing_that_is_live() {
let src = read("crates/assay-core/src/policy_engine.rs");
let live_policy_codes: BTreeSet<String> = src
.lines()
.map(str::trim)
.filter(|l| !l.starts_with("//") && !l.starts_with("pub reason_code"))
.filter_map(|l| l.strip_prefix("reason_code:"))
.filter_map(string_literal)
.collect();
let contradictions: Vec<String> = registered("5.4")
.into_iter()
.filter(|code| live_policy_codes.contains(code))
.filter(|code| code != "E_ARG_SCHEMA" && code != "E_SEQUENCE_VIOLATION")
.collect();
assert!(
contradictions.is_empty(),
"§5.4 lists these as reserved, but `policy_engine` constructs them: {contradictions:?}"
);
}