Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

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
                );
            }
        }
    }
}