Hold every verdict write to a reviewed list of user actions
FR-CULL-13 says evidence never writes a rating, flag, label or trash membership, and nothing enforced it. tools/traceability/src/verdicts.rs parses the shipped code with syn and enumerates every write: calls to the catalog setters and trash recorders, SQL that assigns those columns, sidecar Amendment::Judgement, and fields named rating/flag/label. Each site must be in ALLOWED with a reason, as Input (inside a Slint on_* closure, checked structurally), Relay (its callers are checked in turn), Carried (a verdict made elsewhere: sidecar and XMP pulls, sync merge, catalog mirrored to file, duplicates consolidation) or NotAVerdict. Unlisted sites and stale entries both fail `cargo test -p traceability`; `traces verdicts` prints the list. syn and proc-macro2 were already in the lockfile as proc-macro dependencies; this adds the edges, no new crate and no version change.
This commit is contained in:
@@ -42,6 +42,7 @@ pub mod chord;
|
||||
pub mod gestures;
|
||||
pub mod keymap;
|
||||
pub mod manual;
|
||||
pub mod verdicts;
|
||||
|
||||
/// Requirement ID prefixes that participate in coverage.
|
||||
///
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//! traces gestures-check # gate: non-zero exit if either has drifted
|
||||
//! traces manual # write docs/manual/index.html from its README
|
||||
//! traces manual-check # gate: non-zero exit if the page has drifted
|
||||
//! traces verdicts # list every verdict write; non-zero exit on an unlisted one
|
||||
//! ```
|
||||
//!
|
||||
//! The gesture half scans a different tag out of the same files — see
|
||||
@@ -79,6 +80,9 @@ fn main() -> Result<()> {
|
||||
if mode.starts_with("manual") {
|
||||
return run_manual(&base, mode == "manual-check");
|
||||
}
|
||||
if mode == "verdicts" {
|
||||
return run_verdicts(&base);
|
||||
}
|
||||
|
||||
let req_path = base.join("docs/dev/requirements.md");
|
||||
let markdown = std::fs::read_to_string(&req_path)
|
||||
@@ -248,6 +252,38 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List every verdict write with the reason it is allowed, and fail on any
|
||||
/// that has none — the same check `cargo test -p traceability` runs, in a
|
||||
/// form a reviewer can read ([`traceability::verdicts`]).
|
||||
fn run_verdicts(base: &Path) -> Result<()> {
|
||||
let files = verdicts::sources(base);
|
||||
let report = verdicts::check(&files, verdicts::ALLOWED);
|
||||
println!("files scanned {}", files.len());
|
||||
println!("writes found {}", report.sites.len());
|
||||
for s in &report.sites {
|
||||
let kind = verdicts::ALLOWED
|
||||
.iter()
|
||||
.find(|a| a.file == s.file && a.within == s.within && a.writes == s.writes)
|
||||
.map(|a| format!("{:?}", a.kind))
|
||||
.unwrap_or_else(|| "UNLISTED".into());
|
||||
println!(
|
||||
" {kind:<11} {}:{} {} {} {}",
|
||||
s.file, s.line, s.within, s.writes, s.detail
|
||||
);
|
||||
}
|
||||
if !report.problems.is_empty() {
|
||||
for p in &report.problems {
|
||||
println!(" {p}");
|
||||
}
|
||||
bail!(
|
||||
"{} verdict write problem(s) (FR-CULL-13)",
|
||||
report.problems.len()
|
||||
);
|
||||
}
|
||||
println!("\nverdict gate: PASS");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Render the manual to its page, or check the committed page is that render.
|
||||
fn run_manual(base: &Path, check: bool) -> Result<()> {
|
||||
let source = std::fs::read_to_string(base.join(MANUAL_SOURCE))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user