holger-recipe/src/honesty.rs
//! # The gate's rules — feature honesty, over the manifests, offline
//!
//! Three rules, written in `.nornir/stinky/09-mvp-line-and-feature-honesty.md`
//! §4 and homeless until now. They live HERE, beside [`crate::rules`], because
//! they are predicates over holger's own declaration and holger's own
//! manifests — the same reason `judge_artifact` is code rather than a table.
//!
//! ## Why the house needed them (MEASURED, `542e296`)
//!
//! `.forgejo/workflows/security.yml` is holger's ONLY workflow and its own
//! comment (`:12-13`) says *"no `cargo build`, no `cargo test`, and no
//! `cargo deny`"*. `.github/` holds one Markdown file. **The house has no gate
//! that compiles anything.** The cost is on the record three times:
//!
//! | leak | what rode in | how it was found |
//! |---|---|---|
//! | 1 — `front` | `bench-scenarios → holger-server-lib` (default features) turned `front` on for `full` and `server-local` | by hand, on a deployed binary |
//! | 2 — `auth-passkey` | the same edge, `features = [… "auth-passkey"]` | by hand, a 404 on holger.rs |
//! | 3 — `auth-oidc`/`auth-oauth2` → OpenSSL | the same edge again | by hand, in a dependency review |
//! | 4 — `sign` (TODAY, still RED) | `server/cli/Cargo.toml:71` `server-lib = { …, features = ["sign"] }` | nobody has ever been told |
//!
//! Every one is the same shape: **a feature reached a shipped artifact without
//! any variant naming it.** [`rule1_riders`] is the inverse of that sentence.
//!
//! ## Pure, offline, no process
//!
//! No `Command`, no cargo, no network: the manifests are parsed with `toml` and
//! the feature closure is computed here. The pre-existing guard in
//! `build-space` shells `Command::new(env!("CARGO"))` and **skips silently when
//! cargo is absent**, so a cargo-less runner passed it vacuously. This one
//! cannot be vacuous: it either parses the workspace or it fails.
//!
//! Behind the non-default `gate` feature so the wasm32 cdylib shim
//! (`holger-build-thing`) never links `toml` or `std::fs`.
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::path::{Path, PathBuf};
use anyhow::{anyhow, Context, Result};
// ---------------------------------------------------------------------------
// The lists the rules are written against
// ---------------------------------------------------------------------------
/// Crates whose reason to exist is a test, a benchmark or a fixture.
///
/// A SHIPPED member may depend on these only as `[dev-dependencies]`: cargo
/// never resolves a dev-dependency for a lib/bin build, so such an edge cannot
/// unify a single feature into a shipped graph. `optional = true` is **not**
/// protection — `bench` was optional too, and it was in `default`.
///
/// The owner's ruling, 2026-09-20, in as many words: *"den skall vara
/// dev-beroende, dvs aldrig in i slutbinären"*.
pub const NOT_IN_A_SHIPPED_BINARY: &[&str] = &[
"holger-bench-scenarios",
"nornir-testmatrix",
"testcontainers",
"criterion",
"holger-mannequin",
];
/// Every driver that produces a number the house PUBLISHES.
///
/// An `--example` on a `publish = false` crate is built by nothing in the
/// normal flow, so its rot is silent until someone tries to reproduce a
/// benchmark. MEASURED by the baseline lane: `cargo build --release -p xtask
/// --example nornir-bench` failed at HEAD with **10× `E0063`** — `nornir-bench`
/// 0.1.8 added a field and holger builds that struct by literal at ten sites.
/// The published performance numbers had been unreproducible for weeks.
///
/// The list is MEASURED, not copied. `.nornir/stinky/09-*` §4.3 names
/// `holger-bench-scenarios --example holger_ds --features served`; that example
/// does not exist. `ls bench-scenarios/examples/` on 2026-09-20 is `art_probe`,
/// `pip_probe`, `retention_probe`, `secure_update_bench`, and `xtask/examples/`
/// is `nornir-bench`. Naming a target that is not there makes the gate fail for
/// the wrong reason, which is its own kind of silence. (2026-09-21, ?20: the
/// `holger-ds` driver now exists as a `[[bin]]` of `holger-bench-scenarios` —
/// `--features served --bin holger-ds` — not as an example, so it is still not
/// on this EXAMPLE list.)
///
/// `(package, example, features)`.
pub const BENCH_DRIVERS: &[(&str, &str, &[&str])] = &[
("xtask", "nornir-bench", &[]),
(
"holger-bench-scenarios",
"secure_update_bench",
&["secure-update"],
),
// 2026-10-23 (bench lane): every bench-scenarios example is a driver the gate
// builds (`holger-recipe/tests/bench_examples_build.rs`).
("holger-bench-scenarios", "dataplane_bench", &["dataplane"]),
("holger-bench-scenarios", "art_probe", &["artifactory"]),
("holger-bench-scenarios", "pip_probe", &["served"]),
(
"holger-bench-scenarios",
"retention_probe",
&["retention-planner"],
),
];
/// Examples that exist, are built by nothing, and do NOT compile — recorded so
/// the omission from [`BENCH_DRIVERS`] is a decision rather than a silence.
///
/// MEASURED 2026-09-20 in the declared toolchain container: `art_probe`,
/// `pip_probe` and `retention_probe` each fail with `E0433: cannot find module
/// or crate testcontainers` and `tokio`. They declare no `required-features` in
/// `bench-scenarios/Cargo.toml` (only `secure_update_bench` does, at `:149`),
/// so cargo tries to build them with whatever features happen to be on and they
/// cannot succeed.
///
/// They are not in [`BENCH_DRIVERS`] because they are container probes, not
/// drivers whose numbers the house publishes, and making the gate red for them
/// would be ruling something nobody has ruled. The fix is one line each in
/// `bench-scenarios/Cargo.toml` (`[[example]]` + `required-features`) and it
/// belongs to whoever owns that crate.
/// EMPTY since 2026-10-23: the three probes declare `required-features` and are
/// in [`BENCH_DRIVERS`]. Kept as the place a decision not to build one would be
/// recorded; `holger-recipe/tests/bench_examples_build.rs` keeps it empty.
pub const EXAMPLES_THAT_DO_NOT_BUILD: &[(&str, &str, &str)] = &[];
/// Riders the owner has seen and ACCEPTED, each with a reason. Empty is the goal.
///
/// A row here is a decision on the record, not a silence. `("*", f, …)` accepts
/// the rider on every variant.
///
/// ⚠ `sign` is deliberately NOT here. It is owner question 1 (`stinky/mvpline`
/// §5): either the four server rows declare `sign`, or `server/cli` stops
/// enabling it on the edge. Until that is ruled, [`rule1_riders`] is RED and
/// says so by name — which is the first time anyone has been told.
pub const ACCEPTED_RIDERS: &[(&str, &str, &str)] = &[];
// ---------------------------------------------------------------------------
// The workspace, parsed once
// ---------------------------------------------------------------------------
/// Which `[dependencies]` table an edge came from. RULE 2 reads this.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DepKind {
/// `[dependencies]` — resolved for a lib/bin build. The dangerous one.
Normal,
/// `[dev-dependencies]` — never resolved for a lib/bin build.
Dev,
/// `[build-dependencies]` — a separate feature universe from the target graph.
Build,
}
/// One dependency edge as the manifest declares it.
#[derive(Debug, Clone)]
pub struct DepEdge {
/// The real package name (`package = "…"` when renamed, else the key).
pub package: String,
pub default_features: bool,
pub features: Vec<String>,
pub optional: bool,
pub kind: DepKind,
/// Present ⇒ a workspace-local sibling we can follow.
pub path: Option<String>,
}
/// A workspace member's feature table and dependency edges.
#[derive(Debug, Clone, Default)]
pub struct Manifest {
pub name: String,
/// feature name -> its `["dep:x", "other/feat", "sibling"]` list.
pub features: BTreeMap<String, Vec<String>>,
/// The edge key as written (so `dep:<key>` resolves) -> the edge.
pub deps: BTreeMap<String, DepEdge>,
/// Where the manifest was read from, for error messages.
pub dir: PathBuf,
}
impl Manifest {
/// The edge whose *package* is `pkg`, whatever key it was written under.
fn edge_for_package(&self, pkg: &str) -> Option<&DepEdge> {
self.deps.values().find(|e| e.package == pkg)
}
}
/// Every workspace member, keyed by PACKAGE name. Parsed once; no cargo.
#[derive(Debug, Clone, Default)]
pub struct Workspace {
pub members: BTreeMap<String, Manifest>,
}
impl Workspace {
/// Parse the workspace rooted at `root` (the directory holding the
/// `[workspace]` `Cargo.toml`).
pub fn load(root: &Path) -> Result<Workspace> {
let root_toml = root.join("Cargo.toml");
let text = std::fs::read_to_string(&root_toml)
.with_context(|| format!("reading {}", root_toml.display()))?;
let doc: toml::Value =
toml::from_str(&text).with_context(|| format!("parsing {}", root_toml.display()))?;
let members = doc
.get("workspace")
.and_then(|w| w.get("members"))
.and_then(|m| m.as_array())
.ok_or_else(|| anyhow!("{} declares no [workspace] members", root_toml.display()))?;
let mut ws = Workspace::default();
for m in members {
let pat = m.as_str().unwrap_or_default();
for dir in expand_member(root, pat)? {
let mf =
parse_manifest(&dir).with_context(|| format!("member {}", dir.display()))?;
ws.members.insert(mf.name.clone(), mf);
}
}
Ok(ws)
}
pub fn get(&self, pkg: &str) -> Option<&Manifest> {
self.members.get(pkg)
}
}
/// `"server/repository/*"` -> every subdirectory holding a `Cargo.toml`.
/// Only a trailing `/*` is supported, which is the only form holger uses.
fn expand_member(root: &Path, pat: &str) -> Result<Vec<PathBuf>> {
if let Some(prefix) = pat.strip_suffix("/*") {
let base = root.join(prefix);
let mut out = Vec::new();
let rd = match std::fs::read_dir(&base) {
Ok(rd) => rd,
// A glob that matches nothing is not an error for cargo either.
Err(_) => return Ok(out),
};
for e in rd.flatten() {
let p = e.path();
if p.join("Cargo.toml").is_file() {
out.push(p);
}
}
out.sort();
Ok(out)
} else {
let p = root.join(pat);
if p.join("Cargo.toml").is_file() {
Ok(vec![p])
} else {
Ok(Vec::new())
}
}
}
fn parse_manifest(dir: &Path) -> Result<Manifest> {
let path = dir.join("Cargo.toml");
let text =
std::fs::read_to_string(&path).with_context(|| format!("reading {}", path.display()))?;
let doc: toml::Value =
toml::from_str(&text).with_context(|| format!("parsing {}", path.display()))?;
let name = doc
.get("package")
.and_then(|p| p.get("name"))
.and_then(|n| n.as_str())
.ok_or_else(|| anyhow!("{} has no [package] name", path.display()))?
.to_string();
let mut features = BTreeMap::new();
if let Some(tbl) = doc.get("features").and_then(|f| f.as_table()) {
for (k, v) in tbl {
let list = v
.as_array()
.map(|a| {
a.iter()
.filter_map(|x| x.as_str().map(str::to_string))
.collect()
})
.unwrap_or_default();
features.insert(k.clone(), list);
}
}
let mut deps = BTreeMap::new();
for (table, kind) in [
("dependencies", DepKind::Normal),
("dev-dependencies", DepKind::Dev),
("build-dependencies", DepKind::Build),
] {
collect_deps(&doc, table, kind, &mut deps);
// `[target.'cfg(...)'.dependencies]` — same universe for our purposes.
if let Some(targets) = doc.get("target").and_then(|t| t.as_table()) {
for (_, tv) in targets {
collect_deps(tv, table, kind, &mut deps);
}
}
}
Ok(Manifest {
name,
features,
deps,
dir: dir.to_path_buf(),
})
}
fn collect_deps(
doc: &toml::Value,
table: &str,
kind: DepKind,
out: &mut BTreeMap<String, DepEdge>,
) {
let Some(tbl) = doc.get(table).and_then(|d| d.as_table()) else {
return;
};
for (key, v) in tbl {
let edge = match v {
toml::Value::String(_) => DepEdge {
package: key.clone(),
default_features: true,
features: Vec::new(),
optional: false,
kind,
path: None,
},
toml::Value::Table(t) => DepEdge {
package: t
.get("package")
.and_then(|p| p.as_str())
.unwrap_or(key)
.to_string(),
default_features: t
.get("default-features")
.or_else(|| t.get("default_features"))
.and_then(|b| b.as_bool())
.unwrap_or(true),
features: t
.get("features")
.and_then(|f| f.as_array())
.map(|a| {
a.iter()
.filter_map(|x| x.as_str().map(str::to_string))
.collect()
})
.unwrap_or_default(),
optional: t.get("optional").and_then(|b| b.as_bool()).unwrap_or(false),
kind,
path: t.get("path").and_then(|p| p.as_str()).map(str::to_string),
},
_ => continue,
};
// A dev edge must never overwrite the normal edge of the same name:
// RULE 2 asks whether a NORMAL edge exists, and `agent/cli` declares
// `holger-bench-scenarios` as a dev-dependency ONLY (correct today).
match out.get(key) {
Some(prev) if prev.kind == DepKind::Normal => {}
_ => {
out.insert(key.clone(), edge);
}
}
}
}
// ---------------------------------------------------------------------------
// Feature closures
// ---------------------------------------------------------------------------
/// package -> the features enabled on it.
pub type Closure = BTreeMap<String, BTreeSet<String>>;
/// One unit of work: enable `feature` on `pkg` (or, with `None`, just activate it).
type Job = (String, Option<String>);
/// Walk the feature graph from `roots` on `entry`.
///
/// `follow_edges = false` gives the **DECLARED** closure: only what the row
/// asked for, propagated through feature tables and `dep/feature`
/// implications. `follow_edges = true` gives the **RESOLVED** closure: cargo's
/// real answer, which additionally unions in every activated normal edge's own
/// `features = [...]` and `default` set.
///
/// The difference between the two is exactly the rider set.
fn closure(ws: &Workspace, entry: &str, roots: &[String], follow_edges: bool) -> Closure {
let mut enabled: Closure = BTreeMap::new();
let mut activated: BTreeSet<String> = BTreeSet::new();
let mut q: VecDeque<Job> = VecDeque::new();
q.push_back((entry.to_string(), None));
for r in roots {
q.push_back((entry.to_string(), Some(r.clone())));
}
while let Some((pkg, feat)) = q.pop_front() {
// Only workspace members can be walked: we have no manifest for a
// crates.io crate. Their feature sets are still RECORDED (below) so the
// rider report can name them, they are simply not expanded.
let first_activation = activated.insert(pkg.clone());
if let Some(f) = &feat {
if !enabled.entry(pkg.clone()).or_default().insert(f.clone()) {
continue; // already had it
}
} else if !first_activation {
continue;
}
let Some(mf) = ws.get(&pkg) else { continue };
if first_activation && follow_edges {
// cargo resolves every non-optional normal edge of an activated
// package, with that edge's own features. THIS is where a rider is
// born: `server-lib = { …, features = ["sign"] }`.
for edge in mf.deps.values() {
if edge.kind != DepKind::Normal || edge.optional {
continue;
}
push_edge(&mut q, edge, ws);
}
}
// A THIN PRODUCT is declared by its edge (HOL-PRODUCTS): `holger-downstream` is
// `holger = { features = ["downstream"] }` and nothing else, so the features that
// edge names onto another product crate ARE the declaration, never a rider.
if first_activation && !follow_edges && pkg == entry {
for edge in mf.deps.values() {
if edge.kind == DepKind::Normal
&& !edge.optional
&& crate::variants::PRODUCTS.contains(&edge.package.as_str())
{
push_edge(&mut q, edge, ws);
}
}
}
let Some(f) = feat else { continue };
let Some(items) = mf.features.get(&f) else {
continue;
};
for item in items {
if let Some(dep_key) = item.strip_prefix("dep:") {
if let Some(edge) = mf.deps.get(dep_key) {
if follow_edges {
push_edge(&mut q, edge, ws);
} else {
q.push_back((edge.package.clone(), None));
}
}
} else if let Some((dep_key, sub)) = item.split_once('/') {
let dep_key = dep_key.trim_end_matches('?');
let weak = item
.split_once('/')
.map(|(d, _)| d.ends_with('?'))
.unwrap_or(false);
if let Some(edge) = mf.deps.get(dep_key) {
// `dep/feat` (strong) also ACTIVATES an optional dep.
if !weak && follow_edges {
push_edge(&mut q, edge, ws);
}
q.push_back((edge.package.clone(), Some(sub.to_string())));
} else {
q.push_back((dep_key.to_string(), Some(sub.to_string())));
}
} else if mf.features.contains_key(item) {
q.push_back((pkg.clone(), Some(item.clone())));
} else if let Some(edge) = mf.deps.get(item) {
// 2015-style implicit optional-dependency feature.
if follow_edges {
push_edge(&mut q, edge, ws);
} else {
q.push_back((edge.package.clone(), None));
}
}
}
}
enabled
}
fn push_edge(q: &mut VecDeque<Job>, edge: &DepEdge, ws: &Workspace) {
q.push_back((edge.package.clone(), None));
for f in &edge.features {
q.push_back((edge.package.clone(), Some(f.clone())));
}
if edge.default_features {
// Only a workspace member's `default` can be expanded; for a foreign
// crate the name is recorded and left alone.
if ws
.get(&edge.package)
.map(|m| m.features.contains_key("default"))
.unwrap_or(false)
{
q.push_back((edge.package.clone(), Some("default".to_string())));
}
}
}
/// Every workspace package a shipped row can reach through NORMAL edges only.
pub fn normal_closure_packages(ws: &Workspace, entry: &str) -> BTreeSet<String> {
let mut seen = BTreeSet::new();
let mut q = VecDeque::from([entry.to_string()]);
while let Some(pkg) = q.pop_front() {
if !seen.insert(pkg.clone()) {
continue;
}
let Some(mf) = ws.get(&pkg) else { continue };
for edge in mf.deps.values() {
if edge.kind == DepKind::Normal {
q.push_back(edge.package.clone());
}
}
}
seen
}
// ---------------------------------------------------------------------------
// The variants a rule judges
// ---------------------------------------------------------------------------
/// One shipped row, reduced to what the rules need.
#[derive(Debug, Clone)]
pub struct ShippedRow {
pub variant: String,
/// The workspace package the row builds (`-p`).
pub package: String,
/// The features the ROW declares.
pub features: Vec<String>,
pub no_default_features: bool,
}
/// The rows a rule judges: every declared variant cargo actually compiles into
/// a binary — `kind = exe | service`, with a package this workspace owns.
///
/// `container`/`iso`/`installer`/`vm` rows are composed from those binaries by
/// another door, so judging them here would double-count; `lib`/`wasm` rows
/// ship no binary.
pub fn shipped_rows() -> Vec<ShippedRow> {
use nornir_build_thing::variant::ArtifactKind;
crate::declared()
.variants
.iter()
.filter(|v| matches!(v.kind, ArtifactKind::Exe | ArtifactKind::Service))
.filter_map(|v| {
v.package.as_ref().map(|p| ShippedRow {
variant: v.name.clone(),
package: p.clone(),
features: v.features.clone(),
no_default_features: v.no_default_features,
})
})
.collect()
}
// ---------------------------------------------------------------------------
// RULE 1
// ---------------------------------------------------------------------------
/// One feature that reached a shipped artifact without a variant naming it.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Rider {
pub variant: String,
pub package: String,
pub feature: String,
}
impl std::fmt::Display for Rider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"variant '{}' ships {} feature \"{}\", which NO variant declares. It is \
enabled by a dependency edge, not by the build declaration. Either name it \
on the row, or make the edge stop enabling it (`default-features = false`, \
or a dev-dependency).",
self.variant, self.package, self.feature
)
}
}
/// **RULE 1 — every feature in a shipped artifact must be NAMED by its variant.**
///
/// A feature is *named* when its enablement chain begins at something the ROW
/// declared: an entry in the row's `features`, or the member package's
/// `default` set when `no_default_features = false`. A *rider* is a feature
/// whose chain begins at a bare `[dependencies]` edge — a feature the build
/// DECLARATION does not know it is shipping.
///
/// Judged over every WORKSPACE package in the closure, not just `server-lib`:
/// leak 1 lived in `front`, leak 3's holger half in `auth-oidc`, and a future
/// one will live somewhere else.
pub fn rule1_riders(ws: &Workspace) -> Vec<Rider> {
let mut out = Vec::new();
for row in shipped_rows() {
let Some(entry_mf) = ws.get(&row.package) else {
continue;
};
let mut roots = row.features.clone();
if !row.no_default_features {
if let Some(d) = entry_mf.features.get("default") {
roots.extend(d.clone());
}
}
let named = closure(ws, &row.package, &roots, false);
let actual = closure(ws, &row.package, &roots, true);
for (pkg, feats) in &actual {
// Foreign crates have no manifest here, so their feature sets are
// not comparable — RULE 1's crate-level twin (a native-TLS backend
// in the closure) is a separate rule and not this one.
if ws.get(pkg).is_none() {
continue;
}
let empty = BTreeSet::new();
let named_here = named.get(pkg).unwrap_or(&empty);
for f in feats.difference(named_here) {
if f == "default" {
continue;
}
if ACCEPTED_RIDERS
.iter()
.any(|(v, rf, _)| (*v == row.variant || *v == "*") && rf == f)
{
continue;
}
out.push(Rider {
variant: row.variant.clone(),
package: pkg.clone(),
feature: f.clone(),
});
}
}
}
out.sort();
out.dedup();
out
}
// ---------------------------------------------------------------------------
// RULE 2
// ---------------------------------------------------------------------------
/// A bench/test/fixture crate reachable from a shipped row through a NORMAL edge.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BenchEdge {
pub package: String,
pub dep: String,
pub optional: bool,
/// Set when a dated [`ACCEPTED_BENCH_EDGES`] row covers this edge but its
/// condition has lapsed — so a stale exemption is loud, never silent.
pub lapsed: Option<String>,
}
impl std::fmt::Display for BenchEdge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"'{}' is reachable from a shipped variant and declares '{}' as a NORMAL \
dependency{}. A bench/test crate's manifest must never be able to unify \
features into a shipped graph — move it to [dev-dependencies].{}",
self.package,
self.dep,
if self.optional {
" (optional — which does NOT help: a default feature turns it on)"
} else {
""
},
match &self.lapsed {
Some(w) => format!(
" ⚠ A dated RULE 2 exemption covers this edge but NO LONGER APPLIES: {w}."
),
None => String::new(),
}
)
}
}
/// **RULE 2 — no shipped binary may normal-depend on a bench/test/fixture crate.**
///
/// RULE 1 catches a leak once it exists; RULE 2 forbids the *edge* that makes
/// the class possible.
pub fn rule2_bench_edges(ws: &Workspace) -> Vec<BenchEdge> {
let mut reachable = BTreeSet::new();
for row in shipped_rows() {
reachable.extend(normal_closure_packages(ws, &row.package));
}
let mut out = Vec::new();
for pkg in &reachable {
let Some(mf) = ws.get(pkg) else { continue };
for dep in NOT_IN_A_SHIPPED_BINARY {
if let Some(edge) = mf.edge_for_package(dep) {
if edge.kind == DepKind::Normal && !exemption_holds(ws, dep, edge) {
out.push(BenchEdge {
package: pkg.clone(),
dep: (*dep).to_string(),
optional: edge.optional,
lapsed: exemption_lapse(ws, dep, edge),
});
}
}
}
}
out.sort();
out.dedup();
out
}
// ---------------------------------------------------------------------------
// The report
// ---------------------------------------------------------------------------
/// What the static half of the gate found. RULE 3 is a compile, so it belongs
/// to the door that can run cargo (`cargo xtask gate`).
#[derive(Debug, Clone, Default)]
pub struct Report {
pub riders: Vec<Rider>,
pub bench_edges: Vec<BenchEdge>,
}
impl Report {
pub fn is_green(&self) -> bool {
self.riders.is_empty() && self.bench_edges.is_empty()
}
/// One line per finding, prefixed by the rule that found it.
pub fn lines(&self) -> Vec<String> {
let mut v: Vec<String> = self
.bench_edges
.iter()
.map(|e| format!("RULE 2: {e}"))
.collect();
v.extend(self.riders.iter().map(|r| format!("RULE 1: {r}")));
v
}
}
/// Run RULE 1 and RULE 2 over the workspace rooted at `root`.
pub fn check(root: &Path) -> Result<Report> {
let ws = Workspace::load(root)?;
Ok(Report {
riders: rule1_riders(&ws),
bench_edges: rule2_bench_edges(&ws),
})
}
/// The repo root, found by walking up from `dir` to the `[workspace]` manifest.
pub fn find_workspace_root(dir: &Path) -> Result<PathBuf> {
let mut cur = Some(dir);
while let Some(d) = cur {
let m = d.join("Cargo.toml");
if m.is_file() {
if let Ok(t) = std::fs::read_to_string(&m) {
if t.contains("[workspace]") {
return Ok(d.to_path_buf());
}
}
}
cur = d.parent();
}
Err(anyhow!("no [workspace] Cargo.toml above {}", dir.display()))
}
// ---------------------------------------------------------------------------
// RULE 2 — the dated exemptions, and the condition each one lives under
// ---------------------------------------------------------------------------
/// Bench/test edges the owner has seen and ACCEPTED, each with a date and a
/// measured reason. `(dep, reason)`. Empty is the goal.
///
/// A row here is a decision on the record. It is **not** a mute button: an
/// exemption is honoured only while [`exemption_lapse`] finds nothing wrong
/// with it, and the day the condition breaks the edge is reported again with
/// the condition named. That is the difference between encoding a ruling and
/// deleting a test.
///
/// ⚠ **DATED EXEMPTION, 2026-09-20 (?33) — `nornir-testmatrix`.** Recorded in
/// prose in `server/lib/Cargo.toml`, `server/cli/Cargo.toml` and
/// `agent/cli/Cargo.toml` by b103d4e, which named THREE edges. The gate sees
/// **22**: the same optional-normal edge is declared by `holger-traits`, the
/// world connector and every `znippy-*`/`file-*` repository backend, for the
/// same reason and with the same shape. The prose covered the mechanism and
/// undercounted the sites, so the exemption is encoded here once, by dep.
pub const ACCEPTED_BENCH_EDGES: &[(&str, &str)] = &[(
"nornir-testmatrix",
"2026-09-20 (?33): functional_status is called from LIBRARY and BINARY code, not \
from under #[cfg(test)] — server/lib/src/grpc.rs:193 (every gRPC handler plus the \
RBAC write gate), server/cli/src/main.rs:1095 (every CLI verb), \
agent/cli/src/main.rs:194, traits/src/lib.rs:943 and one call per repository \
backend (e.g. server/repository/znippy-npm/src/lib.rs:397). The move to \
[dev-dependencies] was ATTEMPTED and REVERTED: cargo check -p holger-server-lib \
--features testmatrix then fails with E0433 at grpc.rs:195. Moving the edges would \
delete the verb-level emit that `nornir test --features testmatrix` reads. Safe \
because RULE 1 covers the leak RULE 2 fears: testmatrix is in no default set and \
no variant names it.",
)];
/// Why a listed exemption does **not** apply to this edge, if it does not.
///
/// The two conditions are exactly the ones the ruling names: the edge must
/// still be `optional`, and the dep must still be absent from every shipped
/// row's RESOLVED closure. Either one breaking re-arms RULE 2 for that edge.
pub fn exemption_lapse(ws: &Workspace, dep: &str, edge: &DepEdge) -> Option<String> {
if !ACCEPTED_BENCH_EDGES.iter().any(|(d, _)| *d == dep) {
return None;
}
if !edge.optional {
return Some(format!(
"the exemption assumed '{dep}' stays an OPTIONAL edge, and this one is not"
));
}
if let Some(variant) = row_resolving(ws, dep) {
return Some(format!(
"the exemption assumed no shipped row resolves '{dep}', and variant \
'{variant}' now does — re-examine it, as b103d4e asked"
));
}
None
}
/// Is this edge covered by a LIVE exemption?
fn exemption_holds(ws: &Workspace, dep: &str, edge: &DepEdge) -> bool {
ACCEPTED_BENCH_EDGES.iter().any(|(d, _)| *d == dep) && exemption_lapse(ws, dep, edge).is_none()
}
/// The first shipped variant whose resolved feature closure ACTIVATES `dep`,
/// if any. This is the "added to a default set or named by a row" the ruling
/// says to re-examine on, measured rather than remembered.
fn row_resolving(ws: &Workspace, dep: &str) -> Option<String> {
for row in shipped_rows() {
let Some(entry) = ws.get(&row.package) else {
continue;
};
let mut roots = row.features.clone();
if !row.no_default_features {
if let Some(d) = entry.features.get("default") {
roots.extend(d.clone());
}
}
let enabled = closure(ws, &row.package, &roots, true);
// (a) the dep is in the closure with a feature turned on.
if enabled.contains_key(dep) {
return Some(row.variant);
}
// (b) an activated member has an enabled feature that ACTIVATES the dep
// (`dep:key`, a bare `key`, or a STRONG `key/feat` — `key?/feat` is
// weak and activates nothing).
for (pkg, feats) in &enabled {
let Some(mf) = ws.get(pkg) else { continue };
let keys: Vec<String> = mf
.deps
.iter()
.filter(|(_, e)| e.package == dep)
.map(|(k, _)| k.clone())
.collect();
if keys.is_empty() {
continue;
}
for f in feats {
let Some(items) = mf.features.get(f) else {
continue;
};
for item in items {
for k in &keys {
if item.as_str() == k.as_str()
|| item.as_str() == format!("dep:{k}")
|| item.starts_with(&format!("{k}/"))
{
return Some(row.variant);
}
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn root() -> PathBuf {
find_workspace_root(Path::new(env!("CARGO_MANIFEST_DIR"))).expect("workspace root")
}
/// The engine must actually see holger's workspace — a rule that parses
/// nothing is a rule that passes everything.
#[test]
fn the_workspace_parses_and_is_not_empty() {
let ws = Workspace::load(&root()).expect("workspace");
assert!(
ws.members.len() > 20,
"only {} members parsed — the glob expansion is broken",
ws.members.len()
);
assert!(ws.get("holger").is_some());
assert!(ws.get("holger-server-lib").is_some());
}
/// And it must judge rows — an empty row set is the same vacuous green.
#[test]
fn there_are_shipped_rows_to_judge() {
let rows = shipped_rows();
assert!(!rows.is_empty(), "no shipped rows");
assert!(rows.iter().any(|r| r.variant == "mvp-server-musl"));
}
/// RULE 1 must DISCRIMINATE, or it is noise: a feature the ROW ITSELF
/// declares can never be reported as a rider on that row.
///
/// Note what this does NOT say. `full` IS reported as shipping
/// `holger-server-lib` feature `mini`, and that is correct: `mini` is
/// declared by the `mini` and `mvp-server` rows, and `full`'s own
/// declaration does not name it — it arrives on `full` through the
/// bench-scenarios edge, which is the mechanism of feature leak 1. The rule
/// is per row, not per feature name.
#[test]
fn a_feature_the_row_itself_declares_is_never_a_rider() {
let ws = Workspace::load(&root()).expect("workspace");
let riders = rule1_riders(&ws);
for row in shipped_rows() {
for r in riders.iter().filter(|r| r.variant == row.variant) {
assert!(
!row.features.contains(&r.feature),
"row '{}' declares \"{}\" and it was still reported as a rider",
row.variant,
r.feature
);
}
}
}
}