use std::collections::{BTreeMap, BTreeSet};
use std::path::{Path, PathBuf};
const INVENTORY: &str = "docs/architecture/CLI-JSON-IDENTITIES.md";
const SCANNED: &[&str] = &["crates/assay-cli/src", "crates/assay-core/src"];
const DEPENDENCY_CRATES: &[&str] = &[
"crates/assay-canonical/src",
"crates/assay-common/src",
"crates/assay-evidence/src",
"crates/assay-mcp-server/src",
"crates/assay-metrics/src",
"crates/assay-monitor/src",
"crates/assay-policy/src",
"crates/assay-registry/src",
"crates/assay-runner-core/src",
"crates/assay-runner-linux/src",
"crates/assay-runner-schema/src",
"crates/assay-sim/src",
];
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 recorded(marker: &str) -> Vec<String> {
let doc = read(INVENTORY);
let needle = format!("<!-- machine-checked: {marker} -->");
let after = doc
.split_once(&needle)
.unwrap_or_else(|| panic!("{INVENTORY} has no `{needle}` marker"))
.1;
let block = after
.split_once("```text\n")
.unwrap_or_else(|| panic!("marker `{marker}` is not followed by a ```text block"))
.1;
let block = block
.split_once("\n```")
.unwrap_or_else(|| panic!("marker `{marker}` block is unterminated"))
.0;
block
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
.map(str::to_owned)
.collect()
}
fn non_document_rows() -> BTreeMap<String, String> {
let mut rows = BTreeMap::new();
for line in recorded("not-cli-documents") {
let (ident, reason) = line.split_once('|').unwrap_or_else(|| {
panic!("{INVENTORY}: `not-cli-documents` row is not `identity | reason`: {line:?}")
});
let (ident, reason) = (ident.trim(), reason.trim());
assert!(
!reason.is_empty(),
"{INVENTORY}: {ident:?} is recorded as a non-document with no reason"
);
assert!(
rows.insert(ident.to_string(), reason.to_string()).is_none(),
"{INVENTORY}: `not-cli-documents` records {ident:?} twice"
);
}
rows
}
fn strip_test_modules_at(src: &str, whose: &str) -> String {
let mut out = String::with_capacity(src.len());
let mut rest = src;
while let Some((attr, body)) = find_test_mod(rest) {
out.push_str(&rest[..attr]);
rest = &rest[skip_balanced(rest, body, whose)..];
}
out.push_str(rest);
out
}
fn find_test_mod(src: &str) -> Option<(usize, usize)> {
const ATTR: &str = "#[cfg(test)]";
let mut from = 0usize;
while let Some(rel) = src[from..].find(ATTR) {
let attr = from + rel;
let after = &src[attr + ATTR.len()..];
let trimmed = after.trim_start();
if let Some(tail) = module_after_visibility(trimmed) {
if let Some(brace) = tail.find('{') {
if tail[..brace]
.trim()
.chars()
.all(|c| c.is_alphanumeric() || c == '_')
{
let prefix_len = trimmed.len() - tail.len();
let consumed = after.len() - trimmed.len() + prefix_len + brace + 1;
return Some((attr, attr + ATTR.len() + consumed));
}
}
}
from = attr + ATTR.len();
}
None
}
fn skip_balanced(src: &str, from: usize, whose: &str) -> usize {
let bytes = src.as_bytes();
let mut i = from;
let mut depth = 1usize;
while i < bytes.len() && depth > 0 {
match bytes[i] {
b'{' => depth += 1,
b'}' => depth -= 1,
b'"' => i = skip_string(bytes, i),
b'\'' => i = skip_char(bytes, i),
b'/' if bytes.get(i + 1) == Some(&b'/') => {
while i < bytes.len() && bytes[i] != b'\n' {
i += 1;
}
}
b'/' if bytes.get(i + 1) == Some(&b'*') => {
i += 2;
while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
i += 1;
}
i += 1;
}
_ => {}
}
i += 1;
}
assert_eq!(
depth, 0,
"a `#[cfg(test)] mod` block is never closed in {whose}"
);
i
}
fn skip_string(bytes: &[u8], open: usize) -> usize {
let mut hashes = 0usize;
let mut back = open;
while back > 0 && bytes[back - 1] == b'#' {
hashes += 1;
back -= 1;
}
let raw = back > 0 && bytes[back - 1] == b'r';
let mut i = open + 1;
if raw {
while i < bytes.len() {
if bytes[i] == b'"' && bytes[i + 1..].iter().take(hashes).all(|b| *b == b'#') {
return i + hashes;
}
i += 1;
}
return bytes.len();
}
while i < bytes.len() {
match bytes[i] {
b'\\' => i += 1,
b'"' => return i,
_ => {}
}
i += 1;
}
bytes.len()
}
fn skip_char(bytes: &[u8], open: usize) -> usize {
let mut i = open + 1;
if bytes.get(i) == Some(&b'\\') {
i += 1;
}
match bytes.get(i + 1) {
Some(b'\'') => i + 1,
_ => open,
}
}
fn test_only_files() -> BTreeSet<PathBuf> {
let mut out = BTreeSet::new();
let mut queue: Vec<PathBuf> = Vec::new();
for dir in SCANNED {
let base = workspace_root().join(dir);
let mut stack = vec![base];
while let Some(path) = stack.pop() {
for entry in std::fs::read_dir(&path).expect("read_dir") {
let p = entry.expect("dir entry").path();
if p.is_dir() {
stack.push(p);
} else if p.extension().and_then(|e| e.to_str()) == Some("rs") {
for name in test_mod_declarations(&std::fs::read_to_string(&p).expect("read")) {
queue.extend(module_files(&p, &name));
}
}
}
}
}
while let Some(file) = queue.pop() {
if !out.insert(file.clone()) {
continue;
}
let Ok(src) = std::fs::read_to_string(&file) else {
continue;
};
for name in declared_modules(&src) {
queue.extend(module_files(&file, &name));
}
}
out
}
fn test_mod_declarations(src: &str) -> Vec<String> {
let mut names = Vec::new();
let mut lines = src.lines().peekable();
while let Some(line) = lines.next() {
if line.trim() != "#[cfg(test)]" {
continue;
}
let Some(next) = lines.peek() else { continue };
if let Some(name) = bare_mod_name(next) {
names.push(name);
}
}
names
}
fn declared_modules(src: &str) -> Vec<String> {
src.lines().filter_map(bare_mod_name).collect()
}
fn module_after_visibility(src: &str) -> Option<&str> {
src.strip_prefix("mod ")
.or_else(|| src.strip_prefix("pub mod "))
.or_else(|| src.strip_prefix("pub(crate) mod "))
.or_else(|| src.strip_prefix("pub(super) mod "))
}
fn bare_mod_name(line: &str) -> Option<String> {
let line = line.trim();
let rest = module_after_visibility(line)?;
let name = rest.strip_suffix(';')?;
name.chars()
.all(|c| c.is_alphanumeric() || c == '_')
.then(|| name.to_string())
}
fn module_files(declaring: &std::path::Path, name: &str) -> Vec<PathBuf> {
let dir = if declaring.file_name().and_then(|f| f.to_str()) == Some("mod.rs")
|| declaring.file_name().and_then(|f| f.to_str()) == Some("main.rs")
|| declaring.file_name().and_then(|f| f.to_str()) == Some("lib.rs")
{
declaring.parent().map(|p| p.to_path_buf())
} else {
declaring
.parent()
.map(|p| p.join(declaring.file_stem().and_then(|s| s.to_str()).unwrap_or("")))
};
let Some(dir) = dir else { return Vec::new() };
[
dir.join(format!("{name}.rs")),
dir.join(name).join("mod.rs"),
]
.into_iter()
.filter(|p| p.exists())
.collect()
}
fn identities_in_source() -> BTreeMap<String, String> {
let root = workspace_root();
let test_only = test_only_files();
let mut found: BTreeMap<String, String> = BTreeMap::new();
for dir in SCANNED {
let base = root.join(dir);
let mut stack = vec![base.clone()];
while let Some(path) = stack.pop() {
let entries = std::fs::read_dir(&path)
.unwrap_or_else(|e| panic!("read_dir {}: {e}", path.display()));
for entry in entries {
let entry = entry.expect("dir entry");
let p = entry.path();
if p.is_dir() {
stack.push(p);
} else if p.extension().and_then(|e| e.to_str()) == Some("rs") {
if test_only.contains(&p) {
continue;
}
let src = std::fs::read_to_string(&p)
.unwrap_or_else(|e| panic!("read {}: {e}", p.display()));
let src = strip_test_modules_at(&src, &p.display().to_string());
for (lineno, line) in src.lines().enumerate() {
let line = code_before_comment(line);
if line.trim().is_empty() {
continue;
}
for ident in identities_in_line(line) {
let rel = p.strip_prefix(&root).unwrap_or(&p).display();
found
.entry(ident)
.or_insert_with(|| format!("{rel}:{}", lineno + 1));
}
}
}
}
}
}
for (identity, site) in published_identities_in_dependencies() {
found.entry(identity).or_insert(site);
}
assert!(
!found.is_empty(),
"collected no identities; the scan shape moved"
);
found
}
fn published_identities_in_dependencies() -> BTreeMap<String, String> {
let root = workspace_root();
let mut found = BTreeMap::new();
for dir in DEPENDENCY_CRATES {
let base = root.join(dir);
if !base.exists() {
continue;
}
let mut stack = vec![base];
while let Some(path) = stack.pop() {
for entry in std::fs::read_dir(&path).expect("read_dir") {
let p = entry.expect("dir entry").path();
if p.is_dir() {
stack.push(p);
continue;
}
if p.extension().and_then(|e| e.to_str()) != Some("rs") {
continue;
}
let src = strip_test_modules_at(
&std::fs::read_to_string(&p).expect("read"),
&p.display().to_string(),
);
for (lineno, line) in src.lines().enumerate() {
let line = code_before_comment(line);
if !line.trim_start().starts_with("pub const ") {
continue;
}
for identity in identities_in_line(line) {
let rel = p.strip_prefix(&root).unwrap_or(&p).display();
found
.entry(identity)
.or_insert_with(|| format!("{rel}:{}", lineno + 1));
}
}
}
}
}
found
}
fn code_before_comment(line: &str) -> &str {
let bytes = line.as_bytes();
let mut i = 0usize;
let mut in_string = false;
while i + 1 < bytes.len() {
match bytes[i] {
b'\\' if in_string => i += 1,
b'"' => in_string = !in_string,
b'/' if !in_string && bytes[i + 1] == b'/' => return &line[..i],
_ => {}
}
i += 1;
}
line
}
fn identities_in_line(line: &str) -> Vec<String> {
let mut out = Vec::new();
let bytes = line.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
if bytes[i] != b'"' {
i += 1;
continue;
}
let start = i + 1;
let Some(rel_end) = line[start..].find('"') else {
break;
};
let literal = &line[start..start + rel_end];
i = start + rel_end + 1;
if !literal.starts_with("assay.") {
continue;
}
let Some(last) = literal.rsplit('.').next() else {
continue;
};
let is_generation = last.len() > 1
&& last.starts_with('v')
&& last[1..].chars().all(|c| c.is_ascii_digit());
if !is_generation {
continue;
}
if literal
.chars()
.any(|c| !(c.is_ascii_alphanumeric() || c == '.' || c == '_' || c == '-'))
{
continue;
}
out.push(literal.to_string());
}
out
}
const WRITERS: &[&str] = &[
"write_stdout_json",
"to_string_pretty",
"to_vec_pretty",
"to_writer",
"serde_json::to_string(",
"serde_json::to_vec(",
"std::fs::write",
"tokio::fs::write",
"fs::write",
"write_document",
"write_all",
"writeln!",
"println!",
];
fn document_rows() -> BTreeMap<String, (String, String)> {
let mut rows = BTreeMap::new();
for line in recorded("cli-documents") {
let parts: Vec<&str> = line.split('|').map(str::trim).collect();
assert_eq!(
parts.len(),
3,
"{INVENTORY}: `cli-documents` row is not `identity | writer | namer`: {line:?}"
);
let namer = if parts[2] == "-" { parts[1] } else { parts[2] };
assert!(
rows.insert(
parts[0].to_string(),
(parts[1].to_string(), namer.to_string())
)
.is_none(),
"{INVENTORY}: `cli-documents` records {:?} twice",
parts[0]
);
}
rows
}
#[test]
fn every_production_identity_is_classified() {
let documents: BTreeSet<String> = document_rows().keys().cloned().collect();
let others: BTreeSet<String> = non_document_rows().keys().cloned().collect();
let overlap: Vec<_> = documents.intersection(&others).cloned().collect();
assert!(
overlap.is_empty(),
"{INVENTORY}: these are recorded as both a document and not a document: {overlap:?}"
);
let in_source = identities_in_source();
let recorded: BTreeSet<String> = documents.union(&others).cloned().collect();
let collected: BTreeSet<String> = in_source.keys().cloned().collect();
let unrecorded: Vec<String> = collected
.difference(&recorded)
.map(|id| format!("{id} ({})", in_source[id]))
.collect();
assert!(
unrecorded.is_empty(),
"these identities ship but are in neither block of {INVENTORY}. Add each one to the block \
that says what it is — a document a CLI command emits, or an event / nested object / \
input / digest domain:\n {}",
unrecorded.join("\n ")
);
let stale: Vec<_> = others.difference(&collected).cloned().collect();
assert!(
stale.is_empty(),
"{INVENTORY} records non-documents that no longer appear in production source: {stale:?}"
);
let _ = &recorded;
}
#[test]
fn manifest_promotion_outputs_are_cli_documents() {
let documents = document_rows();
let non_documents = non_document_rows();
const WRITER: &str = "crates/assay-mcp-server/src/manifest_promotion.rs";
for identity in [
"assay.mcp_manifest_candidate.v0",
"assay.declared_mcp_manifest.v0",
] {
assert!(
documents.contains_key(identity),
"{identity} is written by `assay mcp manifest` to a caller-named path and must be in the cli-documents block"
);
assert!(
!non_documents.contains_key(identity),
"{identity} cannot also be classified as a non-document"
);
assert_eq!(
documents.get(identity).map(|(writer, _)| writer.as_str()),
Some(WRITER),
"{identity} must name the module that serializes and creates the document, not the CLI dispatcher"
);
}
}
#[test]
fn documents_without_an_identity_are_recorded_and_still_exist() {
const REQUIRED: &[&str] = &[
"aee_landlock_seal",
"baseline",
"baseline_diff",
"calibration_report",
"coverage_legacy",
"coverage_report",
"discover_inventory",
"evidence_attest_dsse",
"evidence_diff",
"evidence_lint",
"evidence_lint_sarif",
"evidence_list",
"evidence_list_for_run",
"evidence_show",
"evidence_store_status",
"explain_report",
"generated_policy",
"hygiene_report",
"mcp_config_path",
"profile_file",
"profile_perf",
"profile_show",
"run_json_extended",
"run_json_minimal",
"sarif",
"session_state_window",
"signed_tool",
"sim_run_report",
"skill_supply_chain_cdx",
"soak_report",
"trust_basis_generate",
];
let mut rows: BTreeMap<String, (String, String)> = BTreeMap::new();
for line in recorded("unnamed-documents") {
let parts: Vec<&str> = line.split('|').map(str::trim).collect();
assert_eq!(
parts.len(),
3,
"{INVENTORY}: `unnamed-documents` row is not `key | producer | token`: {line:?}"
);
assert!(
rows.insert(
parts[0].to_string(),
(parts[1].to_string(), parts[2].to_string())
)
.is_none(),
"{INVENTORY}: duplicate `unnamed-documents` key {:?}",
parts[0]
);
}
let recorded_keys: BTreeSet<&str> = rows.keys().map(String::as_str).collect();
let required: BTreeSet<&str> = REQUIRED.iter().copied().collect();
let missing: Vec<_> = required.difference(&recorded_keys).collect();
assert!(
missing.is_empty(),
"{INVENTORY} is missing required rows for documents that carry no identity: {missing:?}. \
These cannot be collected from source, so dropping the row would leave no trace."
);
let unexpected: Vec<_> = recorded_keys.difference(&required).collect();
assert!(
unexpected.is_empty(),
"{INVENTORY} records unnamed documents this test does not require: {unexpected:?}. Add \
them to REQUIRED in the same commit, so the row cannot be dropped later without failing."
);
for (key, (producer, token)) in &rows {
let path = workspace_root().join(producer);
let src = std::fs::read_to_string(&path).unwrap_or_else(|e| {
panic!(
"{INVENTORY}: row `{key}` names a producer that cannot be read: {producer} ({e})"
)
});
assert!(
src.contains(token.as_str()),
"{INVENTORY}: row `{key}` says {producer} carries {token:?}, and it does not. The \
producer moved; move the row with it."
);
}
}
#[test]
fn describe_binds_only_recorded_documents() {
let documents: BTreeSet<String> = document_rows().keys().cloned().collect();
let src = read("crates/assay-cli/src/cli/commands/describe/bindings.rs");
let mut constants = BTreeSet::new();
for line in src.lines() {
let line = line.trim();
if let Some(rest) = line.strip_prefix("identity: ") {
constants.insert(rest.trim_end_matches(',').to_string());
}
}
assert!(
!constants.is_empty(),
"parsed no `identity:` bindings; the BINDING_ROWS shape moved"
);
let mut values = BTreeSet::new();
for dir in SCANNED {
let base = workspace_root().join(dir);
let mut stack = vec![base];
while let Some(path) = stack.pop() {
for entry in std::fs::read_dir(&path).expect("read_dir") {
let p = entry.expect("dir entry").path();
if p.is_dir() {
stack.push(p);
} else if p.extension().and_then(|e| e.to_str()) == Some("rs") {
let text = std::fs::read_to_string(&p).expect("read");
for name in &constants {
for line in text.lines() {
if line.contains(&format!("const {name}:")) {
if let Some(v) = identities_in_line(line).into_iter().next() {
values.insert(v);
}
}
}
}
}
}
}
}
assert_eq!(
values.len(),
constants.len(),
"could not resolve every `BINDING_ROWS` identity constant to a literal: {constants:?} \
resolved to {values:?}"
);
let unrecorded: Vec<_> = values.difference(&documents).cloned().collect();
assert!(
unrecorded.is_empty(),
"`assay describe` binds identities the inventory does not record as documents: \
{unrecorded:?}"
);
}
#[test]
fn documents_are_bound_to_a_writer() {
for (identity, (writer, namer)) in document_rows() {
assert_writer_is_about_this_identity(&identity, &writer, &namer);
let naming = read(&namer);
let names_it = naming.contains(&format!("\"{identity}\""))
|| constant_names_for(&identity)
.iter()
.any(|name| naming.contains(name.as_str()));
assert!(
names_it,
"{INVENTORY}: {identity} is recorded as named in {namer}, and that file neither \
contains the literal nor mentions a constant holding it"
);
let writing = read(&writer);
assert!(
WRITERS.iter().any(|w| writing.contains(w)),
"{INVENTORY}: {identity} is recorded as written by {writer}, and that file calls no \
writer ({WRITERS:?}). Either it is not the writer, or it is not a document"
);
}
}
fn constant_names_for(identity: &str) -> Vec<String> {
let mut names = Vec::new();
for dir in SCANNED {
let base = workspace_root().join(dir);
let mut stack = vec![base];
while let Some(path) = stack.pop() {
for entry in std::fs::read_dir(&path).expect("read_dir") {
let p = entry.expect("dir entry").path();
if p.is_dir() {
stack.push(p);
} else if p.extension().and_then(|e| e.to_str()) == Some("rs") {
let text = std::fs::read_to_string(&p).expect("read");
for line in text.lines() {
if !line.contains(&format!("\"{identity}\"")) {
continue;
}
if let Some(rest) = line.trim().split("const ").nth(1) {
if let Some(name) = rest.split(':').next() {
names.push(name.trim().to_string());
}
}
}
}
}
}
}
names
}
#[test]
fn dependency_crates_match_the_manifest() {
let manifest = read("crates/assay-cli/Cargo.toml");
let deps = manifest
.split_once("\n[dependencies]")
.expect("assay-cli manifest has no [dependencies] section")
.1;
let deps = deps.split_once("\n[").map(|(head, _)| head).unwrap_or(deps);
let mut declared = BTreeSet::new();
for line in deps.lines() {
let name = line.split(['.', '=', ' ']).next().unwrap_or("").trim();
if name.starts_with("assay-") {
declared.insert(name.to_string());
}
}
assert!(
!declared.is_empty(),
"parsed no assay-* dependencies; the manifest shape moved"
);
let separately_scanned: BTreeSet<String> = SCANNED
.iter()
.filter_map(|dir| dir.strip_prefix("crates/"))
.filter_map(|rest| rest.strip_suffix("/src"))
.map(str::to_string)
.collect();
let listed: BTreeSet<String> = DEPENDENCY_CRATES
.iter()
.filter_map(|dir| dir.strip_prefix("crates/"))
.filter_map(|rest| rest.strip_suffix("/src"))
.map(str::to_string)
.collect();
let expected: BTreeSet<String> = declared.difference(&separately_scanned).cloned().collect();
assert_eq!(
listed, expected,
"DEPENDENCY_CRATES has drifted from assay-cli/Cargo.toml. Every assay-* dependency is \
either scanned in full (SCANNED) or scanned for pub const identities (DEPENDENCY_CRATES); \
a dependency in neither is a crate whose published identities nothing classifies"
);
}
fn assert_writer_is_about_this_identity(identity: &str, writer: &str, namer: &str) {
let writing = strip_comments(&writer_module_source(writer));
if writer == namer {
let named = writing.contains(&format!("\"{identity}\""))
|| constant_names_for(identity)
.iter()
.any(|name| mentions_token(&writing, name));
assert!(
named,
"{INVENTORY}: {identity} names {writer} as both writer and namer, and that file does \
not mention the identity. Either it is not the writer, or the naming column should \
point at the file that sets the schema"
);
return;
}
let published = published_symbols(namer);
assert!(
!published.is_empty(),
"{INVENTORY}: {identity} names {namer} as its naming file and that file publishes no \
symbols, so nothing can tie the writer to it"
);
let touched: Vec<&String> = published
.iter()
.filter(|symbol| mentions_token(&writing, symbol))
.collect();
assert!(
!touched.is_empty(),
"{INVENTORY}: {identity} is recorded as written by {writer} and named in {namer}, and the \
writer mentions none of that file's public symbols ({published:?}). A file that writes \
some other document and happens to call a writer would look identical"
);
}
fn writer_module_source(writer: &str) -> String {
let root = workspace_root();
let path = root.join(writer);
let mut out = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {writer}: {e}"));
let head = strip_comments(&out);
let test_only: BTreeSet<String> = test_mod_declarations(&head).into_iter().collect();
for name in declared_modules(&head) {
if test_only.contains(&name) {
continue;
}
for file in module_files(&path, &name) {
if let Ok(src) = std::fs::read_to_string(&file) {
out.push('\n');
out.push_str(&src);
}
}
}
out
}
fn strip_comments(src: &str) -> String {
src.lines()
.map(code_before_comment)
.collect::<Vec<_>>()
.join("\n")
}
const TOO_GENERIC: &[&str] = &[
"new",
"default",
"from",
"try_from",
"into",
"parse",
"fmt",
"next",
"len",
"is_empty",
"build",
"builder",
"as_str",
"to_string",
"get",
"insert",
"push",
"run",
];
fn mentions_token(src: &str, token: &str) -> bool {
let mut from = 0usize;
while let Some(rel) = src[from..].find(token) {
let start = from + rel;
let end = start + token.len();
let before_ok = start == 0
|| !src.as_bytes()[start - 1].is_ascii_alphanumeric()
&& src.as_bytes()[start - 1] != b'_';
let after_ok = end >= src.len()
|| !src.as_bytes()[end].is_ascii_alphanumeric() && src.as_bytes()[end] != b'_';
if before_ok && after_ok {
return true;
}
from = start + token.len();
}
false
}
fn published_symbols(path: &str) -> BTreeSet<String> {
let src = read(path);
let mut names = BTreeSet::new();
for line in src.lines() {
let line = code_before_comment(line).trim();
let Some(rest) = line.strip_prefix("pub") else {
continue;
};
let rest = match rest.strip_prefix('(') {
Some(restricted) => match restricted.split_once(')') {
Some((_, after)) => after,
None => continue,
},
None => rest,
};
let Some(rest) = rest.strip_prefix(' ') else {
continue;
};
let rest = rest
.strip_prefix("const ")
.or_else(|| rest.strip_prefix("struct "))
.or_else(|| rest.strip_prefix("enum "))
.or_else(|| rest.strip_prefix("type "))
.or_else(|| rest.strip_prefix("fn "));
let Some(rest) = rest else { continue };
let name: String = rest
.chars()
.take_while(|c| c.is_alphanumeric() || *c == '_')
.collect();
if !name.is_empty() && !TOO_GENERIC.contains(&name.as_str()) {
names.insert(name);
}
}
names
}
const COMMANDS_DIR: &str = "crates/assay-cli/src/cli/commands";
const JSON_SERIALIZER_IDIOMS: &[&str] = &[
"write_stdout_json",
"to_string_pretty",
"to_vec_pretty",
"to_writer",
"serde_json::to_string(",
"serde_json::to_vec(",
];
fn is_external_tests_rs(rel: &str) -> bool {
rel.starts_with(COMMANDS_DIR)
&& rel.ends_with("/tests.rs")
&& rel.as_bytes().get(COMMANDS_DIR.len()) == Some(&b'/')
}
fn posix_rel(path: &std::path::Path, root: &std::path::Path) -> String {
path.strip_prefix(root)
.unwrap_or(path)
.to_str()
.unwrap_or_else(|| panic!("{} is not utf-8", path.display()))
.replace('\\', "/")
}
fn safe_component(name: &std::ffi::OsStr) -> Option<String> {
let s = name.to_str()?;
let ordinary = !s.is_empty()
&& s != "."
&& s != ".."
&& s.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-'));
ordinary.then(|| s.to_string())
}
fn collect_rs_under(root: &Path) -> Vec<PathBuf> {
let meta = std::fs::symlink_metadata(root)
.unwrap_or_else(|e| panic!("unreadable command tree root {}: {e}", root.display()));
assert!(
!meta.file_type().is_symlink(),
"command tree root must not be a symlink: {}",
root.display()
);
assert!(
meta.is_dir(),
"command tree root must be a real directory: {}",
root.display()
);
let mut stack = vec![root.to_path_buf()];
let mut files = Vec::new();
while let Some(dir) = stack.pop() {
let entries = std::fs::read_dir(&dir)
.unwrap_or_else(|e| panic!("unreadable command directory {}: {e}", dir.display()));
for entry in entries {
let entry = entry.expect("dir entry");
let file_type = entry
.file_type()
.unwrap_or_else(|e| panic!("unreadable file type in {}: {e}", dir.display()));
assert!(
!file_type.is_symlink(),
"command tree must not contain symlinks: {} / {:?}",
dir.display(),
entry.file_name()
);
let Some(name) = safe_component(&entry.file_name()) else {
panic!(
"command tree path component is not an ordinary ASCII name: {:?}",
entry.file_name()
);
};
let p = dir.join(&name);
assert!(
p.starts_with(root),
"joined path {} escaped walk root {}",
p.display(),
root.display()
);
if file_type.is_dir() {
stack.push(p);
} else if file_type.is_file() && name.ends_with(".rs") {
files.push(p);
} else if !file_type.is_file() {
panic!("command tree entry is not a regular file or directory: {name}");
}
}
}
files.sort();
files
}
fn command_rs_files() -> Vec<PathBuf> {
collect_rs_under(&workspace_root().join(COMMANDS_DIR))
}
fn source_has_json_serializer(src: &str) -> bool {
JSON_SERIALIZER_IDIOMS
.iter()
.any(|idiom| src.contains(idiom))
}
fn json_serializing_command_files() -> BTreeSet<String> {
let root = workspace_root();
let test_only = test_only_files();
let mut found = BTreeSet::new();
for path in command_rs_files() {
let rel = posix_rel(&path, &root);
if is_external_tests_rs(&rel) {
continue;
}
if test_only.contains(&path) {
continue;
}
let src = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("unreadable production command file {rel}: {e}"));
let src = strip_comments(&strip_test_modules_at(&src, &rel));
if source_has_json_serializer(&src) {
found.insert(rel);
}
}
found
}
fn is_command_tree_path(path: &str) -> bool {
path.starts_with(COMMANDS_DIR) && path.as_bytes().get(COMMANDS_DIR.len()) == Some(&b'/')
}
fn command_paths_named_by_inventory_rows() -> BTreeSet<String> {
let mut named = BTreeSet::new();
for (writer, namer) in document_rows().into_values() {
if is_command_tree_path(&writer) {
named.insert(writer);
}
if is_command_tree_path(&namer) {
named.insert(namer);
}
}
for line in recorded("unnamed-documents") {
let parts: Vec<&str> = line.split('|').map(str::trim).collect();
assert_eq!(
parts.len(),
3,
"{INVENTORY}: `unnamed-documents` row is not `key | producer | token`: {line:?}"
);
let producer = parts[1];
if is_command_tree_path(producer) {
named.insert(producer.to_string());
}
}
named
}
fn json_writer_opt_outs() -> BTreeMap<String, String> {
let mut rows = BTreeMap::new();
for line in recorded("json-writer-opt-outs") {
let (path, motive) = line.split_once('|').unwrap_or_else(|| {
panic!("{INVENTORY}: `json-writer-opt-outs` row is not `path | motive`: {line:?}")
});
let (path, motive) = (path.trim(), motive.trim());
assert!(
is_command_tree_path(path),
"{INVENTORY}: opt-out path must be the exact command-tree path, not a basename: {path:?}"
);
assert!(
!motive.is_empty(),
"{INVENTORY}: opt-out {path:?} has an empty motive; name the emit or helper role"
);
assert!(
rows.insert(path.to_string(), motive.to_string()).is_none(),
"{INVENTORY}: `json-writer-opt-outs` records {path:?} twice"
);
}
rows
}
#[test]
fn json_serializing_command_files_are_accounted_for() {
let writers = json_serializing_command_files();
assert!(
!writers.is_empty(),
"collected no JSON-serializing command files; the scan shape moved"
);
let named = command_paths_named_by_inventory_rows();
let opt_outs = json_writer_opt_outs();
let opted: BTreeSet<String> = opt_outs.keys().cloned().collect();
let also_named: Vec<_> = opted.intersection(&named).cloned().collect();
assert!(
also_named.is_empty(),
"{INVENTORY}: these opt-outs are already named by a `cli-documents` writer/namer or \
`unnamed-documents` producer, so the opt-out is stale:\n {}",
also_named.join("\n ")
);
let missing: Vec<_> = opted
.iter()
.filter(|path| !workspace_root().join(path).is_file())
.cloned()
.collect();
assert!(
missing.is_empty(),
"{INVENTORY}: these opt-out paths do not exist:\n {}",
missing.join("\n ")
);
let no_longer_serialize: Vec<_> = opted.difference(&writers).cloned().collect();
assert!(
no_longer_serialize.is_empty(),
"{INVENTORY}: these opt-outs no longer serialize JSON through the converse idioms:\n {}",
no_longer_serialize.join("\n ")
);
let accounted: BTreeSet<String> = named.union(&opted).cloned().collect();
let unaccounted: Vec<String> = writers.difference(&accounted).cloned().collect();
assert!(
unaccounted.is_empty(),
"these production command files serialize JSON and are named by no `cli-documents` \
writer/namer, no `unnamed-documents` producer, and no `json-writer-opt-outs` row:\n {}",
unaccounted.join("\n ")
);
}
#[test]
fn external_tests_rs_serializers_are_not_candidates() {
let root = workspace_root();
let mut tests_rs_with_idioms = Vec::new();
for path in command_rs_files() {
let rel = posix_rel(&path, &root);
if !is_external_tests_rs(&rel) {
continue;
}
let src =
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("unreadable {rel}: {e}"));
if source_has_json_serializer(&src) {
tests_rs_with_idioms.push(rel);
}
}
assert!(
!tests_rs_with_idioms.is_empty(),
"no `*/tests.rs` under {COMMANDS_DIR} currently contains a serializer idiom; the \
exclusion would be an omission rather than a tested rule"
);
let candidates = json_serializing_command_files();
let leaked: Vec<_> = tests_rs_with_idioms
.into_iter()
.filter(|rel| candidates.contains(rel))
.collect();
assert!(
leaked.is_empty(),
"external `*/tests.rs` files leaked into writer candidates: {leaked:?}"
);
}
#[test]
fn inline_cfg_test_module_serializers_are_not_candidates() {
let root = workspace_root();
let test_only = test_only_files();
let mut inline_only = Vec::new();
for path in command_rs_files() {
let rel = posix_rel(&path, &root);
if is_external_tests_rs(&rel) || test_only.contains(&path) {
continue;
}
let src =
std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("unreadable {rel}: {e}"));
let with_tests = strip_comments(&src);
let without_tests = strip_comments(&strip_test_modules_at(&src, &rel));
if source_has_json_serializer(&with_tests) && !source_has_json_serializer(&without_tests) {
inline_only.push(rel);
}
}
assert!(
!inline_only.is_empty(),
"no production command file currently keeps its serializer only inside an inline \
`#[cfg(test)] mod`; the exclusion would be an omission rather than a tested rule"
);
let candidates = json_serializing_command_files();
let leaked: Vec<_> = inline_only
.into_iter()
.filter(|rel| candidates.contains(rel))
.collect();
assert!(
leaked.is_empty(),
"inline `#[cfg(test)]` serializers leaked into writer candidates: {leaked:?}"
);
}
#[test]
fn external_tests_rs_rule_is_a_commands_path_suffix() {
assert!(is_external_tests_rs(
"crates/assay-cli/src/cli/commands/replay/tests.rs"
));
assert!(!is_external_tests_rs(
"crates/assay-cli/src/cli/commands/evidence/attest.rs"
));
assert!(!is_external_tests_rs(
"crates/assay-core/src/report/tests.rs"
));
assert!(!is_external_tests_rs(
"crates/assay-cli/src/cli/commands/tests_helpers.rs"
));
}
#[test]
fn strip_test_modules_removes_pub_crate_mod() {
let src = "fn prod() {}\n#[cfg(test)]\npub(crate) mod tests {\n let _ = serde_json::to_string(&1);\n}\n";
let stripped = strip_test_modules_at(src, "fixture");
assert!(
!stripped.contains("serde_json::to_string("),
"visibility-prefixed test modules must strip like bare `mod tests`"
);
assert!(stripped.contains("fn prod"));
}
#[test]
fn command_tree_component_rule_refuses_anything_that_could_leave_the_directory() {
use std::ffi::OsStr;
for ok in ["mod.rs", "lint.rs", "skill_supply_chain.rs", "a.b-c_1"] {
assert_eq!(
safe_component(OsStr::new(ok)).as_deref(),
Some(ok),
"{ok} is an ordinary name"
);
}
for bad in ["", ".", "..", "../etc", "a/b", "a\\b", "a b", "naïve"] {
assert_eq!(
safe_component(OsStr::new(bad)),
None,
"{bad:?} must not be admitted as a component"
);
}
}
#[test]
fn command_tree_walk_collects_a_new_unreferenced_rs_file() {
let dir = tempfile::tempdir().expect("tempdir");
let nested = dir.path().join("evidence");
std::fs::create_dir(&nested).expect("nested dir");
std::fs::write(dir.path().join("mod.rs"), "fn g() {}\n").expect("mod.rs");
std::fs::write(
nested.join("unreferenced.rs"),
"fn f() { let _ = serde_json::to_string(&1); }\n",
)
.expect("unreferenced.rs");
let files = collect_rs_under(dir.path());
let names: Vec<String> = files
.iter()
.map(|p| {
p.strip_prefix(dir.path())
.expect("under root")
.to_str()
.expect("utf-8")
.replace('\\', "/")
})
.collect();
assert!(
names.contains(&"mod.rs".to_string()),
"existing file must remain visible: {names:?}"
);
assert!(
names.contains(&"evidence/unreferenced.rs".to_string()),
"a newly added command file must be collected so the converse detector can fail it: {names:?}"
);
let with_idiom: Vec<_> = files
.iter()
.filter(|p| source_has_json_serializer(&std::fs::read_to_string(p).expect("read fixture")))
.collect();
assert_eq!(
with_idiom.len(),
1,
"the new file is the serializer the inventory has not named"
);
}
#[cfg(unix)]
#[test]
fn command_tree_walk_rejects_symlinks_fail_closed() {
let dir = tempfile::tempdir().expect("tempdir");
let outside = tempfile::tempdir().expect("outside");
std::fs::write(dir.path().join("ok.rs"), "fn x() {}\n").expect("ok.rs");
std::os::unix::fs::symlink(outside.path(), dir.path().join("escape")).expect("symlink");
let result = std::panic::catch_unwind(|| collect_rs_under(dir.path()));
assert!(
result.is_err(),
"a symlink in the command tree must fail closed, not be followed or skipped"
);
}
#[cfg(unix)]
#[test]
fn command_tree_walk_rejects_symlink_root_fail_closed() {
let holder = tempfile::tempdir().expect("holder");
let outside = tempfile::tempdir().expect("outside");
std::fs::write(outside.path().join("escaped.rs"), "fn x() {}\n").expect("escaped.rs");
let commands = holder.path().join("commands");
std::os::unix::fs::symlink(outside.path(), &commands).expect("root symlink");
let result = std::panic::catch_unwind(|| collect_rs_under(&commands));
assert!(
result.is_err(),
"a symlink supplied as the walk root must fail closed, not be followed"
);
}