1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
//! The reason-code inventory in `docs/architecture/REASON-CODE-VOCABULARIES.md` is checked here.
//!
//! Three vocabularies write to a SARIF `ruleId`, and nothing held them together: a code could be
//! added to one and the others would not notice. This test makes the inventory the record, and
//! makes updating it the way a new code gets added.
//!
//! It lives in `assay-cli` because that is the only crate depending on both `assay-core` and
//! `assay-evidence`.
use std::collections::BTreeSet;
use std::path::PathBuf;
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()))
}
/// The members recorded under a `<!-- machine-checked: NAME -->` marker in the inventory.
///
/// Anchored on an HTML comment rather than on a heading so the prose around it can be rewritten
/// without silently changing what is checked.
fn recorded(marker: &str) -> BTreeSet<String> {
let doc = read("docs/architecture/REASON-CODE-VOCABULARIES.md");
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()
}
/// Every `pub const` in `assay_core::errors::diagnostic::codes`, by its *value*.
///
/// Parsed from source rather than read from a list in the crate, because a list is exactly the
/// thing that drifts: a `pub const` added beside it would not appear, and the check would report
/// coverage it does not have.
fn diagnostic_codes() -> BTreeSet<String> {
let src = read("crates/assay-core/src/errors/diagnostic.rs");
let body = module_body(&src, "pub mod codes {");
let mut found = BTreeSet::new();
for line in body.lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("pub const ") else {
continue;
};
let value = rest
.split_once('=')
.and_then(|(_, v)| string_literal(v))
.unwrap_or_else(|| panic!("`pub const` in `codes` is not a string literal: {line}"));
assert!(
found.insert(value.clone()),
"duplicate code value {value:?} in `codes`"
);
}
assert!(!found.is_empty(), "parsed no codes; the module shape moved");
found
}
/// Every `reason_code:` value constructed in `assay_core::policy_engine`.
///
/// These reach `Diagnostic.code` verbatim (`validate/mod.rs:219`) and so become SARIF `ruleId`s,
/// which is why they belong in the inventory despite not being a registry.
///
/// A `reason_code:` whose value is not a string literal is a hard failure, not a skip. A parser
/// that quietly ignored a computed code would keep passing while the surface it claims to cover
/// grew, which is the failure mode this whole file exists to prevent.
fn policy_engine_codes() -> BTreeSet<String> {
let src = read("crates/assay-core/src/policy_engine.rs");
let mut found = BTreeSet::new();
for line in src.lines() {
let trimmed = line.trim();
// Skip the struct field declaration and any doc comment mentioning the name.
if trimmed.starts_with("//") || trimmed.starts_with("pub reason_code") {
continue;
}
let Some(rest) = trimmed.strip_prefix("reason_code:") else {
continue;
};
let value = string_literal(rest).unwrap_or_else(|| {
panic!(
"policy_engine `reason_code:` is not a string literal, so the inventory cannot \
cover it: {trimmed}"
)
});
found.insert(value);
}
assert!(
!found.is_empty(),
"parsed no policy-engine reason codes; the construction shape moved"
);
found
}
/// The body of a module, from its opening line to the matching brace.
fn module_body<'a>(src: &'a str, header: &str) -> &'a str {
let start = src
.find(header)
.unwrap_or_else(|| panic!("source has no `{header}`"))
+ header.len();
let rest = &src[start..];
let mut depth = 1usize;
for (idx, ch) in rest.char_indices() {
match ch {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
return &rest[..idx];
}
}
_ => {}
}
}
panic!("`{header}` is unterminated");
}
/// The first double-quoted literal in `s`, with no escape handling — reason codes have none, and
/// a literal containing one would fail the inventory comparison rather than pass silently.
fn string_literal(s: &str) -> Option<String> {
let start = s.find('"')? + 1;
let end = s[start..].find('"')? + start;
Some(s[start..end].to_owned())
}
/// Every `pub const` string in `assay_core::report::exercised`.
///
/// These reach the `warnings` array of `run.json` / `summary.json`, which is a different field from
/// `reason_code` and a different surface from a SARIF `ruleId`. Parsed the same way as `codes::` and
/// for the same reason: a second `pub const` added beside the first would otherwise reach a
/// published artifact with nothing recording it.
fn run_json_warning_codes() -> BTreeSet<String> {
// `pub const` strings in that module that are deliberately not codes.
//
// An allow-list rather than a pattern-only filter. A pattern alone ("looks like `W_…`") would
// let a code with an unforeseen prefix slip past unrecorded, which is the exact drift this file
// exists to catch. So every `pub const` string must be either a recorded code or named here.
const NOT_A_CODE: &[&str] = &[
// A `details` key, not a vocabulary member: it names the field the runner writes assertion
// covers to, and it never appears in the `warnings` array.
"assertions_not_exercised",
];
let src = read("crates/assay-core/src/report/exercised.rs");
let mut found = BTreeSet::new();
for line in src.lines() {
let line = line.trim();
let Some(rest) = line.strip_prefix("pub const ") else {
continue;
};
let Some((_, value)) = rest.split_once('=') else {
continue;
};
// Only string-valued constants can be codes. A numeric bound is not a vocabulary member,
// and treating one as a missing entry would make this check noise.
let Some(value) = string_literal(value) else {
continue;
};
if NOT_A_CODE.contains(&value.as_str()) {
continue;
}
assert!(
found.insert(value.clone()),
"duplicate warning code value {value:?}"
);
}
assert!(
!found.is_empty(),
"parsed no run.json warning codes; the module shape moved"
);
found
}
fn lint_rule_ids() -> BTreeSet<String> {
assay_evidence::lint::rules::RULES
.iter()
.map(|rule| rule.id.to_owned())
.collect()
}
#[test]
fn inventory_records_every_diagnostic_code() {
assert_eq!(
diagnostic_codes(),
recorded("diagnostic-codes"),
"`assay_core::errors::diagnostic::codes` and the inventory disagree. A code that reaches \
a SARIF ruleId must be recorded in docs/architecture/REASON-CODE-VOCABULARIES.md, and \
recording it is where the decision about the other vocabularies on that surface goes."
);
}
#[test]
fn inventory_records_every_policy_engine_code() {
assert_eq!(
policy_engine_codes(),
recorded("policy-engine-codes"),
"`assay_core::policy_engine` verdict codes and the inventory disagree. These are forwarded \
verbatim into `Diagnostic::new` at validate/mod.rs:219 and become SARIF ruleIds under the \
same tool driver as `codes::`, so a new one is a new public id."
);
}
#[test]
fn inventory_records_every_lint_rule_id() {
assert_eq!(
lint_rule_ids(),
recorded("lint-rule-ids"),
"`assay_evidence::lint::rules::RULES` and the inventory disagree."
);
}
#[test]
fn inventory_records_every_run_json_warning_code() {
assert_eq!(
run_json_warning_codes(),
recorded("run-json-warning-codes"),
"`assay_core::report::exercised` and the inventory disagree. A code reaching the \
`warnings` array of run.json is a published id and belongs in \
docs/architecture/REASON-CODE-VOCABULARIES.md."
);
}
/// The `warnings` codes are deliberately not in `codes::`, and that must stay a decision.
///
/// `codes::` is inventoried as the SARIF `ruleId` vocabulary under `tool.driver.name = "assay"`.
/// The `run` path does build `Diagnostic`s — the trace client, the assertion matchers, the pipeline
/// error classifier — but none of them reaches `build_sarif_diagnostics`, whose only non-test caller
/// is `assay validate --format sarif`. So a code moved into that registry to tidy it up would be
/// recorded on a surface it never reaches: the inventory would state something false, and this file
/// is the reason the inventory is trusted. If a run-path diagnostic ever gains a route to
/// `build_sarif_diagnostics`, move the constant and delete this test in the same change.
#[test]
fn the_run_json_warning_codes_are_not_in_the_sarif_registry() {
let warnings = run_json_warning_codes();
let sarif = diagnostic_codes();
let overlap: Vec<&String> = warnings.intersection(&sarif).collect();
assert!(
overlap.is_empty(),
"these codes are in both `report::exercised` and `codes::`, so the inventory records them \
on a SARIF surface they do not reach: {overlap:?}"
);
}
#[test]
fn the_two_sarif_rule_id_spaces_stay_disjoint() {
let assay_driver: BTreeSet<String> = diagnostic_codes()
.union(&policy_engine_codes())
.cloned()
.collect();
let lint_driver = lint_rule_ids();
let overlap: Vec<&String> = assay_driver.intersection(&lint_driver).collect();
// They are separate namespaces today, because the driver names differ ("assay" against
// "assay-evidence-lint"). Asserted anyway so that unifying the driver names is a decision
// someone makes, not a collision someone finds afterwards in Code Scanning.
assert!(
overlap.is_empty(),
"these ids exist in both SARIF ruleId vocabularies: {overlap:?}"
);
}
#[test]
fn the_generic_test_result_rule_id_shadows_nothing() {
// `write_sarif` emits `"ruleId": "assay"` for every test result, under the same
// `tool.driver.name` that `build_sarif_diagnostics` uses. A reason code spelled `assay` would
// merge with the entire test-result stream in one Code Scanning alert.
const GENERIC: &str = "assay";
assert!(
!diagnostic_codes().contains(GENERIC),
"a `codes::` member is spelled {GENERIC:?}, which is the generic test-result ruleId"
);
assert!(
!policy_engine_codes().contains(GENERIC),
"a policy-engine reason code is spelled {GENERIC:?}, which is the generic test-result ruleId"
);
assert!(
!lint_rule_ids().contains(GENERIC),
"a lint rule id is spelled {GENERIC:?}, which is the generic test-result ruleId"
);
}