nornir-testmatrix 0.2.1

Reusable, multi-aspect Rust test-matrix engine: wrap a repo's native cargo test/nextest plus build/clippy/fmt/audit/coverage/doctest aspects, parse the results into rows, and ship them to any TestSink. Pure std + serde — no iceberg, arrow, eframe. The portable core of nornir's `nornir test` matrix.
Documentation
//! # nornir-testmatrix
//!
//! The portable core of nornir's **multi-aspect test matrix** — extracted from
//! `nornir` (EPIC L) so any leaf repo (znippy, skade, lbzip2-rs …) can run the
//! same matrix with a *tiny* dep set: `std` + `serde`/`serde_json` + `anyhow`.
//! No iceberg, arrow, eframe, tantivy, or skade.
//!
//! ## What it does
//! 1. **Runner** ([`runner`]) — wrap a repo's *native* Rust test framework
//!    (`cargo nextest run --message-format libtest-json` if nextest is on PATH,
//!    else plain `cargo test`), parse each test's pass/fail + duration, enforce a
//!    stall watchdog. Pure subprocess driving — no test registration.
//! 2. **Multi-aspect engine** ([`aspect`]) — beyond unit tests, run MANY
//!    *aspects* of a repo's health ([`Aspect`]): build, doctest, clippy, fmt,
//!    audit, bench-smoke, coverage, feature-powerset, msrv, examples. Each
//!    shells the right `cargo` subcommand, parses its result, and emits
//!    [`TestResultRow`]s carrying an `aspect` tag + a numeric `metric` (coverage
//!    %, warning count, advisory count …). A missing tool is a **skip**, never a
//!    hard fail.
//! 3. **Model** ([`model`]) — the pure row/summary types ([`TestResultRow`],
//!    [`RunSummary`], [`TestSelector`], the [`status`] tags) + the renderers
//!    ([`render_matrix`], [`summarize_runs`], [`rows_to_json`]).
//! 4. **Sinks** ([`sink`]) — the [`TestSink`] trait + built-in [`JsonFileSink`]
//!    and [`NullSink`] so a leaf repo with no warehouse can still persist its
//!    matrix. nornir implements `TestSink` over its Iceberg warehouse.
//! 5. **Discovery** ([`discover`]) — the autonom (AUT2) anti-drift core: pure
//!    enumerators that build the testable [`Surface`] from data (facett
//!    components, viz tabs × {thin,fat}, CLI subcommands, MCP tools, and the
//!    unreached-function set from `symbol_facts − test-reachable(call_edges)`),
//!    then [`compute_gap`] differences it against coverage so the gate can
//!    enforce `Gap == ∅`. No warehouse here — the caller feeds the rows.
//! 6. **Functional-status mode** ([`functional`]) — a component reports whether
//!    it *actually works* (`functional_status(component, check, ok, detail)`)
//!    from its own headless-render / self-test path. The
//!    [`Aspect::Functional`] runner drains those into rows so a broken render is
//!    a RED matrix row WITHOUT eyeballing a GUI. Gated behind the **`testmatrix`
//!    cargo feature**: release builds (feature off) strip the emit to a no-op.
//!
//! ## How a leaf repo uses it
//! ```no_run
//! use nornir_testmatrix::{run_full_matrix, Aspect, JsonFileSink, TestSink};
//! use std::path::Path;
//!
//! let aspects = [Aspect::Build, Aspect::Unit, Aspect::Doctest, Aspect::Clippy, Aspect::Fmt];
//! let rows = run_full_matrix(Path::new("."), &aspects);
//! let sink = JsonFileSink::new("target/nornir-testmatrix.json");
//! sink.append(&rows).unwrap();
//! ```

pub mod aspect;
pub mod coverage;
pub mod discover;
pub mod functional;
pub mod model;
pub mod runner;
pub mod sink;

// ─── flat re-exports (the crate's public façade) ───────────────────────────

pub use aspect::{
    aspect_label, parse_aspect, parse_funnel_decompose, run_aspect, run_full_matrix, Aspect,
    AspectOutcome,
};
pub use coverage::{
    rows_for, seed_allowlist, stale_allowlist_entries, AllowEntry, Allowlist, CoverageRow,
    CoverageSummary, GateReport, Verdict,
};
pub use discover::{
    cli_commands, compute_gap, facett_components, mcp_tools, test_reachable, unreached_functions,
    viz_tabs, CallEdge, FacetRow, Gap, Mode, Surface, SurfaceKind, SurfaceNode, SymbolRow,
};
pub use functional::{drain_functional_rows, functional_row, functional_status};
pub use model::{
    listed_rows, new_run_id, parse_cargo_test_list, parse_nextest_list, render_matrix,
    rows_to_json, short_run, status, summarize_runs, RunSummary, TestResultRow, TestSelector,
};
pub use runner::{
    detect_runner, list_tests, run_matrix, stall_secs, MatrixRun, Runner, TestCase,
    DEFAULT_STALL_SECS,
};
pub use sink::{JsonFileSink, NullSink, TestSink};