crates/dwarves-embed/src/benchesview.rs
//! **The dwarves TESTMATRIX + BENCHES pane** (task #66) — the two things a human
//! watches while the 100-bench mimir epic (#62) grinds green: the workspace's
//! **testmatrix** (surface × coverage, green ⟺ covered) and its **bench trend**
//! (the `nornir.mimir.<populate|search>.<op>_<size>` populate + MCP-search timings,
//! newest-vs-first).
//!
//! ## Two REUSE consumptions (no twins)
//! * **testmatrix cells** — the SHARED [`facett_matrix::MatrixView`] grid, reused
//! verbatim (L5). It takes [`nornir_testmatrix::TestResultRow`]s and paints the
//! repo×aspect coloured grid; nothing here re-implements a grid.
//! * **bench results** — the project-agnostic [`nornir_bench`] read API
//! ([`nornir_bench::history::read_all`] → `Vec<BenchRun>`) consumed as DATA over the
//! file seam, exactly as the Release pane reads its convergence file. `nornir-bench`
//! carries no egui (there is no bench *viewer* to reuse), so the trend render is
//! dwarves-owned — fed by the read API, never a twinned harness.
//!
//! ## The seam (offline, host-agnostic)
//! Like the Release pane, this reads the artifacts a nornir run writes for the SELECTED
//! workspace: its testmatrix JSONL ([`workspace_testmatrix_path`], a
//! [`nornir_testmatrix::JsonFileSink`]) and its bench history JSONL
//! ([`workspace_bench_history_path`]). No server, no gRPC.
//!
//! ## Both fixtures are gone (audit 2026-09-10)
//! Until 2026-09-10 a missing file did not mean an empty pane: the matrix half fell
//! back to `testmatrix_fixture()` — a hand-written repo×aspect surface over
//! facett/nornir/dwarves/znippy/korp with a red unit cell and a red clippy cell — and
//! the bench half to `bench_fixture()`, TWO invented mimir runs of six benches where
//! the newer one is 1.35× faster, so **the trend column showed ▲ on every row the
//! moment the tab opened**. That green-arrow chart is a measurement claim; nothing had
//! been measured. A `source_matrix`/`source_benches` field said `"fixture"` beside it,
//! and nobody reads a provenance field off a chart that is already telling them the
//! numbers went up.
//!
//! Now: rows or nothing. An unreadable/absent file gives an EMPTY grid and an EMPTY
//! trend, and the half draws the shared [`facett_core::empty::EmptyState`] NAMING the
//! file it wanted (`state_json` carries it as `matrix_empty` / `bench_empty`). The two
//! fixture builders survive only inside `#[cfg(test)]`, where no shipped constructor
//! can reach them.
use std::path::{Path, PathBuf};
use facett_core::empty::EmptyState;
use facett_core::{Facet, Panel};
use facett_matrix::MatrixView;
use nornir_bench::{unit_of, BenchRun, MetricDirection};
use nornir_testmatrix::sink::JsonFileSink;
use nornir_testmatrix::TestResultRow;
/// The `nornir.mimir.` bench-name prefix — the epic-#62 populate/search family this
/// pane trends. A bench whose name starts with this is a mimir bench.
pub const MIMIR_PREFIX: &str = "nornir.mimir.";
/// Sanitize a workspace name to a filesystem-safe token (`[A-Za-z0-9._-]`, else `_`) —
/// the SAME rule the Release pane's `workspace_convergence_path` uses, kept in step.
fn safe(name: &str) -> String {
name.trim()
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-') {
c
} else {
'_'
}
})
.collect()
}
/// The **testmatrix JSONL** a `nornir test` run writes for a named workspace — the
/// per-workspace analogue of the Release pane's convergence path. A
/// [`nornir_testmatrix::JsonFileSink`] history file; empty name ⇒ a shared default.
/// MUST match the matrix emit site so the pane sees the run's rows for that workspace.
pub fn workspace_testmatrix_path(workspace: &str) -> PathBuf {
let name = safe(workspace);
let file = if name.is_empty() {
"dwarves-testmatrix.jsonl".to_string()
} else {
format!("dwarves-testmatrix-{name}.jsonl")
};
std::env::temp_dir().join(file)
}
/// The **bench history JSONL** a `nornir bench run` writes for a named workspace — the
/// `nornir_bench` append-only history the read API reconstructs runs from. Empty name ⇒
/// a shared default. MUST match the bench emit site so the pane trends that workspace.
pub fn workspace_bench_history_path(workspace: &str) -> PathBuf {
let name = safe(workspace);
let file = if name.is_empty() {
"dwarves-bench-history.jsonl".to_string()
} else {
format!("dwarves-bench-history-{name}.jsonl")
};
std::env::temp_dir().join(file)
}
/// One bench's trend across the read runs (oldest→newest): its parsed identity, the
/// primary metric it is trended on, and the per-run value series. Derived purely from
/// the `nornir_bench` read API — no new harness.
#[derive(Debug, Clone)]
pub struct BenchSeries {
/// The full bench name, e.g. `nornir.mimir.populate.symbol_facts_1k`.
pub name: String,
/// `populate` | `search` (the mimir op group).
pub group: String,
/// The operation, e.g. `symbol_facts` / `hybrid_rrf` (name minus group + size).
pub op: String,
/// The size token, e.g. `1k` / `100k` / `1m`.
pub size: String,
/// The metric key the series trends on (a `*_per_sec` throughput, else `p50_us`).
pub metric_key: String,
/// Whether bigger (`High`) or smaller (`Low`) is better for `metric_key`.
pub direction: MetricDirection,
/// The per-run values of `metric_key` (oldest→newest); one point per run that has
/// this bench.
pub history: Vec<f64>,
}
impl BenchSeries {
/// Newest value (the "now").
pub fn latest(&self) -> f64 {
self.history.last().copied().unwrap_or(0.0)
}
/// Oldest value (the baseline the trend is measured from).
pub fn first(&self) -> f64 {
self.history.first().copied().unwrap_or(0.0)
}
/// True iff the newest value is an IMPROVEMENT over the first, per `direction`
/// (throughput up, or latency down). Neutral / single-point ⇒ false.
pub fn improved(&self) -> bool {
if self.history.len() < 2 {
return false;
}
match self.direction {
MetricDirection::High => self.latest() > self.first(),
MetricDirection::Low => self.latest() < self.first(),
MetricDirection::Neutral => false,
}
}
}
/// Pick a bench result's **primary metric**: prefer a throughput key (`*_per_sec`,
/// bigger-is-better), else the p50 latency (`p50_us`), else the first numeric metric.
/// Returns `(key, value, direction)`.
fn primary_metric(
metrics: &serde_json::Map<String, serde_json::Value>,
) -> Option<(String, f64, MetricDirection)> {
// 1) a throughput key.
if let Some((k, v)) = metrics
.iter()
.find(|(k, _)| k.ends_with("_per_sec"))
.and_then(|(k, v)| v.as_f64().map(|v| (k.clone(), v)))
{
let dir = unit_of(&k)
.map(|u| u.direction)
.unwrap_or(MetricDirection::High);
return Some((k, v, dir));
}
// 2) p50 latency.
if let Some(v) = metrics.get("p50_us").and_then(|v| v.as_f64()) {
return Some(("p50_us".to_string(), v, MetricDirection::Low));
}
// 3) any numeric metric (last resort).
metrics
.iter()
.find_map(|(k, v)| v.as_f64().map(|v| (k.clone(), v)))
.map(|(k, v)| {
let dir = unit_of(&k)
.map(|u| u.direction)
.unwrap_or(MetricDirection::Neutral);
(k, v, dir)
})
}
/// Parse a mimir bench name into `(group, op, size)`. `nornir.mimir.populate.symbol_facts_1k`
/// → `("populate", "symbol_facts", "1k")`. Returns `None` for a non-mimir name.
fn parse_mimir_name(name: &str) -> Option<(String, String, String)> {
let rest = name.strip_prefix(MIMIR_PREFIX)?; // populate.symbol_facts_1k
let (group, op_size) = rest.split_once('.')?; // populate , symbol_facts_1k
// size is the trailing `_<token>`; op is everything before it.
let (op, size) = op_size.rsplit_once('_').unwrap_or((op_size, ""));
Some((group.to_string(), op.to_string(), size.to_string()))
}
/// Build the per-bench trend series from a run history (oldest→newest), keeping only
/// the `nornir.mimir.*` benches. The single derive path (used by the pane + tests).
pub fn mimir_series(runs: &[BenchRun]) -> Vec<BenchSeries> {
use std::collections::BTreeMap;
// name → (group, op, size, metric_key, direction, values-in-run-order)
let mut acc: BTreeMap<String, BenchSeries> = BTreeMap::new();
for run in runs {
for res in &run.results {
if !res.name.starts_with(MIMIR_PREFIX) || res.is_static() {
continue;
}
let Some((group, op, size)) = parse_mimir_name(&res.name) else {
continue;
};
let Some((key, val, dir)) = primary_metric(&res.metrics) else {
continue;
};
let entry = acc.entry(res.name.clone()).or_insert_with(|| BenchSeries {
name: res.name.clone(),
group,
op,
size,
metric_key: key.clone(),
direction: dir,
history: Vec::new(),
});
entry.history.push(val);
}
}
acc.into_values().collect()
}
/// **The Testmatrix + Benches pane.** Holds the reused [`MatrixView`] (testmatrix half)
/// and the derived mimir [`BenchSeries`] (bench half), both bound to one workspace. A
/// [`Facet`] any egui host drops into a tab (the `Facet: Panel` blanket makes it a
/// `Box<dyn Panel>`).
pub struct BenchesView {
/// The reused facett-matrix grid (testmatrix cells).
matrix: MatrixView,
/// The derived per-bench trend (mimir populate + search).
benches: Vec<BenchSeries>,
/// The number of bench runs read (the trend depth).
run_count: usize,
/// The workspace this pane is bound to (`""` = none → the shared default files).
workspace: String,
/// Whether the testmatrix half read real rows off a matrix-run file.
from_matrix_run: bool,
/// Whether the bench half read real runs off a bench-history file.
from_bench_run: bool,
/// Set when the matrix file yielded no rows — the NAMED empty state (which file,
/// why), drawn in place of the grid. This is what replaced `testmatrix_fixture()`.
matrix_empty: Option<EmptyState>,
/// Set when the bench history yielded no runs — the NAMED empty state drawn in
/// place of the trend. This is what replaced `bench_fixture()`'s invented ▲ chart.
bench_empty: Option<EmptyState>,
}
/// The testmatrix half's feed, named the way an operator would go look it up.
const MATRIX_FEED: &str = "nornir-testmatrix JsonFileSink";
/// The bench half's feed.
const BENCH_FEED: &str = "nornir-bench history JSONL";
impl BenchesView {
/// Build the pane for `workspace` by reading its testmatrix + bench-history files.
/// The ONE construction path used by [`benches_panel`].
pub fn for_workspace(workspace: &str) -> Self {
let tm = workspace_testmatrix_path(workspace);
let bh = workspace_bench_history_path(workspace);
Self::from_paths(&tm, &bh, workspace)
}
/// Build from explicit paths (the helper the tab + tests drive).
///
/// An absent or empty file gives an EMPTY half plus the NAMED empty state for it —
/// never a fixture, and in particular never an invented bench trend. Never panics.
pub fn from_paths(testmatrix: &Path, bench_history: &Path, workspace: &str) -> Self {
let where_ = |ws: &str| {
if ws.trim().is_empty() {
"no workspace selected".to_string()
} else {
format!("workspace `{ws}`")
}
};
// ── testmatrix half: reuse MatrixView over the persisted rows ──
let rows = JsonFileSink::new(testmatrix.to_path_buf())
.read_all()
.unwrap_or_default();
let from_matrix_run = !rows.is_empty();
// An EMPTY grid, titled — the shared component still owns the render, it simply
// has nothing to paint. The `with_expected` roster is deliberately NOT applied
// here: seeding expected repos with no run would paint a full grid of "not-run"
// cells, which is a claim about which repos exist, made up by this pane.
let (matrix, matrix_empty) = if rows.is_empty() {
(
matrix_from_rows(Vec::new()),
Some(
EmptyState::no_rows(
MATRIX_FEED,
format!(
"{} holds no rows for {}",
testmatrix.display(),
where_(workspace)
),
)
.with_hint("run the suite (`nornir test`), or ▶ Run robot-UI suite below"),
),
)
} else {
(matrix_from_rows(rows), None)
};
// ── bench half: consume the nornir_bench read API ──
let runs = nornir_bench::history::read_all(bench_history).unwrap_or_default();
let from_bench_run = !runs.is_empty();
let run_count = runs.len();
let bench_empty = runs.is_empty().then(|| {
EmptyState::no_rows(
BENCH_FEED,
format!(
"{} holds no runs for {}",
bench_history.display(),
where_(workspace)
),
)
.with_hint("record one: `nornir bench run`")
});
// With no runs this is an empty Vec — so the trend grid draws NO rows and no
// arrows. The deleted `bench_fixture()` used to hand it two runs 1.35× apart.
let benches = mimir_series(&runs);
Self {
matrix,
benches,
run_count,
workspace: workspace.to_string(),
from_matrix_run,
from_bench_run,
matrix_empty,
bench_empty,
}
}
/// Re-bind to a new workspace (the selector's `{"set_workspace":…}` broadcast) —
/// reload both halves from that workspace's files. Never panics.
pub fn bind(&mut self, workspace: &str) {
*self = Self::for_workspace(workspace);
}
/// The workspace the pane is bound to (`""` = none → fixtures).
pub fn workspace(&self) -> &str {
&self.workspace
}
fn count_group(&self, group: &str) -> usize {
self.benches.iter().filter(|b| b.group == group).count()
}
/// **RUN the robot-UI suite from the matrix** — the tab's run-from-UI action. Runs
/// [`run_robot_ui`] in-process and lands ITS rows (no row for a check it did not run)
/// so the grid shows the fresh
/// pass/fail: to the SELECTED workspace's testmatrix sink (then re-binds from it) when
/// bound, else merged into the in-memory matrix. Sets `from_matrix_run` so the header
/// flips to "● matrix run". Never panics.
pub fn run_robot_ui_from_matrix(&mut self) {
let ts = now_micros();
let run_id = format!("robotui-{ts}");
// ONLY the rows this run produced. It used to add three PASS rows for checks it
// never ran (`build`, `unit d::t1`, `bench-smoke`) — and wrote them to the
// workspace's testmatrix sink — so that "latest run wins" kept those columns
// lit. A column this run did not measure is not this run's to colour.
let rows = run_robot_ui(&run_id, ts);
let ws = self.workspace.clone();
if !ws.trim().is_empty() {
let path = workspace_testmatrix_path(&ws);
let _ = append_matrix_rows(&path, &rows);
self.bind(&ws);
} else {
// No workspace: merge this fresh run into whatever REAL rows the grid
// already holds. The `with_expected` roster that used to be re-seeded here
// (dwarves/facett/korp/nornir/znippy) is gone with the fixture — it painted
// a not-run cell for four repos this run never touched.
let mut kept: Vec<TestResultRow> = self
.matrix
.state()
.rows
.iter()
.filter(|r| r.repo != "dwarves")
.cloned()
.collect();
kept.extend(rows);
self.matrix = matrix_from_rows(kept);
self.from_matrix_run = true;
// A real run landed ⇒ the matrix half is no longer empty.
self.matrix_empty = None;
}
}
}
/// Microseconds since the Unix epoch (UTC) — the run timestamp for a fresh matrix run.
fn now_micros() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_micros() as i64)
.unwrap_or(0)
}
impl Facet for BenchesView {
fn title(&self) -> &str {
"Testmatrix + Benches"
}
fn ui(&mut self, ui: &mut egui::Ui) {
// ── honest header: which workspace, real-run vs fixture (plain words) ──
ui.horizontal(|ui| {
if self.workspace.is_empty() {
ui.label("Testmatrix + Benches: no workspace selected");
} else {
ui.label(format!("Testmatrix + Benches: {}", self.workspace));
}
});
ui.horizontal(|ui| {
ui.label(if self.from_matrix_run {
"● matrix run"
} else {
"○ no matrix rows"
});
ui.separator();
ui.label(if self.from_bench_run {
"● bench history"
} else {
"○ no bench runs"
});
});
// ── run-from-the-matrix: the robot-UI suite over the forge panes ──
if ui.button("▶ Run robot-UI suite").on_hover_text(
"Drive the dwarves forge panes headless (state_json) and land the pass/fail as robot-ui rows",
).clicked()
{
self.run_robot_ui_from_matrix();
}
ui.separator();
// ── testmatrix half: the reused MatrixView grid ──
ui.heading("Testmatrix");
if let Some(e) = &self.matrix_empty {
// No rows ⇒ the NAMED file, not a hand-written repo×aspect surface.
e.ui(ui);
} else {
for msg in self.matrix.view(ui) {
let _ = self.matrix.update(msg);
}
}
ui.separator();
// ── bench half: the mimir populate + search trend ──
ui.heading("Benches — nornir.mimir (populate + search)");
if let Some(e) = &self.bench_empty {
// No runs ⇒ NO trend column. A ▲ with nothing measured behind it is the
// exact thing this replaced.
e.ui(ui);
return;
}
ui.label(format!(
"{} benches · {} populate · {} search · {} run(s)",
self.benches.len(),
self.count_group("populate"),
self.count_group("search"),
self.run_count
));
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.show(ui, |ui| {
egui::Grid::new("mimir-bench-grid")
.striped(true)
.spacing([12.0, 4.0])
.show(ui, |ui| {
ui.label(egui::RichText::new("group").strong());
ui.label(egui::RichText::new("op").strong());
ui.label(egui::RichText::new("size").strong());
ui.label(egui::RichText::new("metric").strong());
ui.label(egui::RichText::new("latest").strong());
ui.label(egui::RichText::new("trend").strong());
ui.end_row();
for b in &self.benches {
ui.label(&b.group);
ui.label(&b.op);
ui.label(&b.size);
ui.label(&b.metric_key);
ui.label(format!("{:.0}", b.latest()));
let (arrow, col) = if b.history.len() < 2 {
("·", egui::Color32::GRAY)
} else if b.improved() {
("▲", egui::Color32::from_rgb(90, 200, 120))
} else if b.latest() == b.first() {
("=", egui::Color32::GRAY)
} else {
("▼", egui::Color32::from_rgb(224, 90, 90))
};
ui.label(egui::RichText::new(arrow).color(col).monospace());
ui.end_row();
}
});
});
}
fn state_json(&self) -> serde_json::Value {
let benches: Vec<serde_json::Value> = self
.benches
.iter()
.map(|b| {
serde_json::json!({
"name": b.name,
"group": b.group,
"op": b.op,
"size": b.size,
"metric_key": b.metric_key,
"latest": b.latest(),
"first": b.first(),
"history_len": b.history.len(),
"improved": b.improved(),
})
})
.collect();
let mut j = serde_json::json!({
"component": "BenchesView",
"selected_workspace": self.workspace,
"release_target": self.workspace,
// `"fixture"` is gone from both of these — the only two values now are the
// real run and the honest empty, and the empty carries its own named block.
"source_matrix": if self.from_matrix_run { "workspace-run" } else { "empty" },
"source_benches": if self.from_bench_run { "workspace-run" } else { "empty" },
"matrix_feed": MATRIX_FEED,
"bench_feed": BENCH_FEED,
// the reused MatrixView's own rich state (repo_count · rows[].cells[] · totals)
"matrix": Facet::state_json(&self.matrix),
"bench_count": self.benches.len(),
"populate_count": self.count_group("populate"),
"search_count": self.count_group("search"),
"run_count": self.run_count,
"benches": benches,
});
if let Some(e) = &self.matrix_empty {
j["matrix_empty"] = e.state_json();
}
if let Some(e) = &self.bench_empty {
j["bench_empty"] = e.state_json();
}
j
}
fn update_json(&mut self, msg_json: &str) {
if let Ok(v) = serde_json::from_str::<serde_json::Value>(msg_json) {
// The selector's broadcast: re-bind both halves to the new workspace.
if let Some(ws) = v.get("set_workspace").and_then(|t| t.as_str()) {
self.bind(ws);
return;
}
// RUN-from-the-matrix (the robot-observable seam a robot/host drives): run the
// robot-UI suite over the forge panes and land the rows so the grid refreshes.
if v.get("run_robot_ui").and_then(|t| t.as_bool()) == Some(true) {
self.run_robot_ui_from_matrix();
return;
}
}
// Otherwise forward to the reused grid (repo drill-in select/clear, etc.).
Facet::update_json(&mut self.matrix, msg_json);
}
}
/// The dwarves UI **Testmatrix + Benches** tab for a workspace: reads that workspace's
/// testmatrix + bench-history files (fixtures on absence). Host-agnostic + offline.
pub fn benches_panel(workspace: &str) -> Box<dyn Panel> {
Box::new(BenchesView::for_workspace(workspace))
}
/// Build the pane from explicit file paths (a host/CLI that already resolved them, or a
/// test). Missing files ⇒ fixtures.
pub fn benches_panel_at(
testmatrix: &Path,
bench_history: &Path,
workspace: &str,
) -> Box<dyn Panel> {
Box::new(BenchesView::from_paths(
testmatrix,
bench_history,
workspace,
))
}
// ── fixtures (the never-blank fallback + the robot oracle) ────────────────────
/// Build a [`MatrixView`] from persisted rows, titled for this pane.
fn matrix_from_rows(rows: Vec<TestResultRow>) -> MatrixView {
let mut v = MatrixView::new("Testmatrix").with_rows(rows);
v.set_title("Testmatrix");
v
}
/// The aspect columns the dwarves Testmatrix ALWAYS shows — the six core aspects plus
/// `bench-smoke` (benchers) and `robot-ui` (accesskit pane-drive scenarios), so tests +
/// benchers + robot-UI suites are visible as columns even before a run (not-run cells).
/// `robot-ui` is unioned in here (it is deliberately not in facett's `ASPECT_DEFAULT`).
pub const DWARVES_ASPECTS: &[&str] = &[
"build",
"unit",
"doctest",
"clippy",
"fmt",
"audit",
"bench-smoke",
"robot-ui",
];
/// One matrix row (small local builder; the pane's rows are otherwise read from a sink).
#[cfg(test)]
fn tr(run: &str, repo: &str, aspect: &str, name: &str, st: &str, ts: i64) -> TestResultRow {
TestResultRow {
run_id: run.into(),
repo: repo.into(),
suite: repo.into(),
test_name: name.into(),
status: st.into(),
duration_ms: 1.0,
ts_micros: ts,
message: if nornir_testmatrix::status::is_red(st) {
format!("{name} failed")
} else {
String::new()
},
aspect: aspect.into(),
metric: 0.0,
}
}
/// A testmatrix surface for a TEST — a realistic repo×aspect spread (greens, a failing
/// unit cell, a failing clippy cell, a robot-UI suite with one red scenario, and an
/// expected-but-unrun repo) that the grid tests paint against.
///
/// **`#[cfg(test)]` since 2026-09-10.** This was `pub fn testmatrix_fixture()` and it
/// was what the SHIPPED pane drew whenever the workspace's matrix file was missing.
/// It is now unreachable from every shipped constructor, which is the whole point: a
/// pane with no rows draws the named empty state instead.
#[cfg(test)]
pub(crate) fn testmatrix_fixture() -> MatrixView {
use nornir_testmatrix::status::{FAIL, PASS};
let rows = vec![
// facett: unit fails one case; bench-smoke + robot-UI green.
tr("fx", "facett", "build", "build", PASS, 100),
tr("fx", "facett", "unit", "f::t1", PASS, 100),
tr("fx", "facett", "unit", "f::t2", FAIL, 100),
tr("fx", "facett", "clippy", "clippy", PASS, 100),
tr("fx", "facett", "bench-smoke", "bench_smoke", PASS, 100),
tr("fx", "facett", "robot-ui", "matrix_tab_drives", PASS, 100),
tr("fx", "facett", "robot-ui", "stat_tab_drives", PASS, 100),
// nornir: clippy reds; unit + bench-smoke + robot-UI green.
tr("fx", "nornir", "build", "build", PASS, 100),
tr("fx", "nornir", "unit", "n::t1", PASS, 100),
tr("fx", "nornir", "clippy", "clippy", FAIL, 100),
tr("fx", "nornir", "bench-smoke", "bench_smoke", PASS, 100),
tr("fx", "nornir", "robot-ui", "arch_tab_drives", PASS, 100),
tr("fx", "nornir", "robot-ui", "release_tab_drives", PASS, 100),
tr("fx", "nornir", "robot-ui", "funnel_tab_drives", PASS, 100),
// dwarves: the forge's OWN robot-UI suite — one scenario red (honest, not all-green).
tr("fx", "dwarves", "build", "build", PASS, 100),
tr("fx", "dwarves", "unit", "d::t1", PASS, 100),
tr("fx", "dwarves", "bench-smoke", "bench_smoke", PASS, 100),
tr("fx", "dwarves", "robot-ui", "benches_tab_drives", PASS, 100),
tr(
"fx",
"dwarves",
"robot-ui",
"coverage_tab_drives",
PASS,
100,
),
tr("fx", "dwarves", "robot-ui", "release_tab_drives", PASS, 100),
tr("fx", "dwarves", "robot-ui", "cicd_tab_drives", PASS, 100),
tr("fx", "dwarves", "robot-ui", "forge_tab_drives", FAIL, 100),
// znippy: a lone doctest.
tr("fx", "znippy", "doctest", "doc", PASS, 100),
];
let mut v = MatrixView::new("Testmatrix")
.with_rows(rows)
// `korp` is expected but never ran → a full row of NotRun cells; the aspect surface
// always shows bench-smoke + robot-ui columns even before a run.
.with_expected(
["dwarves", "facett", "korp", "nornir", "znippy"]
.iter()
.map(|s| s.to_string())
.collect(),
DWARVES_ASPECTS,
);
v.set_title("Testmatrix");
v
}
/// TWO mimir bench runs for a TEST, the newer 1.35x faster, so the trend derivation
/// has an improvement to find.
///
/// **`#[cfg(test)]` since 2026-09-10.** As `pub fn bench_fixture()` this was the
/// SHIPPED pane's fallback, which meant opening the Benches tab with no bench history
/// drew a six-row trend with a green up-arrow on every line. Nothing had been measured.
/// A test may invent numbers; a chart may not.
#[cfg(test)]
pub(crate) fn bench_fixture() -> Vec<BenchRun> {
fn res(name: &str, per_sec_key: &str, per_sec: f64, p50_us: f64) -> nornir_bench::BenchResult {
let mut m = serde_json::Map::new();
m.insert(per_sec_key.into(), serde_json::json!(per_sec));
m.insert("p50_us".into(), serde_json::json!(p50_us));
m.insert("p99_us".into(), serde_json::json!(p50_us * 3.0));
nornir_bench::BenchResult::measured(name, m)
}
let run = |date: &str, mult: f64| BenchRun {
id: nornir_bench::BenchRun::new_id(),
date: date.into(),
timestamp: Some(format!("{date}T00:00:00Z")),
version: "0.5.4".into(),
machine: "Loki".into(),
cores: 12,
machine_facts: None,
results: vec![
res(
"nornir.mimir.populate.symbol_facts_1k",
"rows_per_sec",
120_000.0 * mult,
8.0 / mult,
),
res(
"nornir.mimir.populate.call_edge_index_10k",
"edges_per_sec",
90_000.0 * mult,
11.0 / mult,
),
res(
"nornir.mimir.populate.ragnar_build_1k",
"keys_per_sec",
300_000.0 * mult,
3.0 / mult,
),
res(
"nornir.mimir.search.hybrid_rrf_1k",
"queries_per_sec",
4_000.0 * mult,
250.0 / mult,
),
res(
"nornir.mimir.search.hybrid_rrf_100k",
"queries_per_sec",
900.0 * mult,
1100.0 / mult,
),
res(
"nornir.mimir.search.dense_topk_10k",
"queries_per_sec",
2_500.0 * mult,
400.0 / mult,
),
],
tests: Vec::new(),
};
// newer run is 1.35× faster — trend ▲ on every throughput bench.
vec![run("2026-07-10", 1.0), run("2026-07-17", 1.35)]
}
// ── RUN-from-the-matrix: the robot-UI suite over the dwarves forge panes ──────────
/// Build `robot-ui`-aspect [`TestResultRow`]s from named scenario verdicts — the pure
/// map `(scenario, passed, note)` → a matrix row, so the matrix shows each robot-UI
/// scenario's pass/fail. `repo` groups them (the forge under test).
pub fn robot_ui_rows(
run_id: &str,
repo: &str,
ts_micros: i64,
results: &[(&str, bool, &str)],
) -> Vec<TestResultRow> {
use nornir_testmatrix::status::{FAIL, PASS};
results
.iter()
.map(|(name, passed, note)| TestResultRow {
run_id: run_id.to_string(),
repo: repo.to_string(),
suite: "robot-ui".into(),
test_name: (*name).to_string(),
status: if *passed { PASS } else { FAIL }.into(),
duration_ms: 0.0,
ts_micros,
message: if *passed {
String::new()
} else {
(*note).to_string()
},
aspect: "robot-ui".into(),
metric: 0.0,
})
.collect()
}
/// **RUN the dwarves robot-UI suite in-process** — the honest "run from the matrix"
/// action. It constructs each dwarves forge pane with NO feed, reads the pane's own
/// `state_json` (the SAME robot-observable seam a `nornir-robotui` scenario drives),
/// asserts a documented invariant, and returns one `robot-ui` [`TestResultRow`] per pane
/// with a real pass/fail. No network, no accesskit backend — a self-contained pane-drive
/// over the forge's panes; a broken pane reds its row (never a fabricated green).
/// `repo` = the forge these panes belong to (`"dwarves"`).
///
/// **What the checks assert changed on 2026-09-10.** They used to assert the panes were
/// FULL with no feed present — `repo_count >= 3`, `planes == 3`, `session_count >= 1`,
/// `table_count >= 1` — which is a test that the fixtures still exist, and would go red
/// the moment the fixtures were deleted, for the best possible reason. Each check now
/// asserts the invariant that actually matters and survives: **rows or a NAMED empty
/// state, never neither and never both** (`facett_core::empty::EmptyState`, feed +
/// cause) — see the `rows_or_named_empty` helper inside this function.
///
/// Note it is NOT "the pane is empty". A suite that demanded emptiness would pass only
/// on a forge that has never released, imported a tape, or recorded a bench, and go RED
/// on one that has — a fabricated red, which is the same sin as the fabricated green
/// the fixtures were deleted for, pointing the other way.
pub fn run_robot_ui(run_id: &str, ts_micros: i64) -> Vec<TestResultRow> {
/// **Rows or a NAMED empty — never neither, and never both.**
///
/// `key` is where the pane's [`EmptyState`] block rides in its state, `rows` the
/// count of whatever that half actually read. Honest means: rows present ⇒ NO empty
/// block; no rows ⇒ a block with `empty` set and a non-blank `feed`.
///
/// Deliberately NOT "the pane is empty". Asserting emptiness would make this row
/// depend on whether this particular box happens to have a convergence/tape/bench
/// file lying around: the same suite would go RED on a forge that has done a
/// release — a fabricated red, which is the mirror of the fabricated green the
/// fixtures were deleted for. This predicate is the invariant the 2026-09-10
/// removal actually established, and it holds in both worlds.
fn rows_or_named_empty(st: &serde_json::Value, key: &str, rows: u64) -> (bool, String) {
let e = &st[key];
let feed = e["feed"].as_str().unwrap_or("");
let named = e["empty"] == serde_json::Value::Bool(true) && !feed.is_empty();
let ok = if rows > 0 { e.is_null() } else { named };
(
ok,
format!("{key}: rows={rows} named_empty={named} feed={feed:?}"),
)
}
// Each check: (scenario name, a closure returning (ok, note)). Wrapped in
// catch_unwind so a panicking pane becomes a RED row, not a lost run.
type Check = (&'static str, fn() -> (bool, String));
let checks: &[Check] = &[
("benches_tab_names_its_empty_feeds", || {
let st = benches_panel("").state_json();
let (m_ok, m_note) = rows_or_named_empty(
&st,
"matrix_empty",
st["matrix"]["repo_count"].as_u64().unwrap_or(0),
);
let (b_ok, b_note) =
rows_or_named_empty(&st, "bench_empty", st["bench_count"].as_u64().unwrap_or(0));
(
st["component"] == "BenchesView" && m_ok && b_ok,
format!("{m_note} {b_note}"),
)
}),
("release_tab_names_its_empty_feed", || {
let st = crate::release_panel(None).state_json();
let (ok, note) = rows_or_named_empty(
&st,
"convergence_empty",
st["node_count"].as_u64().unwrap_or(0),
);
(st["component"] == "ReleaseView" && ok, note)
}),
("cicd_wizard_generates_valid_dag", || {
let st = Facet::state_json(&crate::CicdWizardPanel::new("robot-ci"));
let ok = st["pipeline_valid"] == serde_json::Value::Bool(true)
&& st["dag_nodes"].as_u64().unwrap_or(0) >= 1;
(ok, format!("dag_nodes={}", st["dag_nodes"]))
}),
("saga_tab_names_its_empty_tape", || {
let st = crate::saga_panel("").state_json();
let (ok, note) =
rows_or_named_empty(&st, "tape_empty", st["session_count"].as_u64().unwrap_or(0));
(st["component"] == "SagaView" && ok, note)
}),
("warehouse_health_tab_names_its_empty_feed", || {
// Was `table_count >= 1` over a DELETED file — which is a test that the
// warehouse fixture still exists, and it passed only because an unfed pane
// invented a populated warehouse. Same invariant as the others now.
let p = std::env::temp_dir().join("dwarves-embed-robotui-nohealth.json");
let _ = std::fs::remove_file(&p);
let st = crate::warehouse_health_panel_at(&p, "").state_json();
let (ok, note) =
rows_or_named_empty(&st, "health_empty", st["table_count"].as_u64().unwrap_or(0));
(st["component"] == "WarehouseHealthView" && ok, note)
}),
];
let results: Vec<(&str, bool, String)> = checks
.iter()
.map(
|(name, f)| match std::panic::catch_unwind(std::panic::AssertUnwindSafe(*f)) {
Ok((ok, note)) => (*name, ok, note),
Err(_) => (*name, false, "pane panicked".to_string()),
},
)
.collect();
let borrowed: Vec<(&str, bool, &str)> = results
.iter()
.map(|(n, ok, note)| (*n, *ok, note.as_str()))
.collect();
robot_ui_rows(run_id, "dwarves", ts_micros, &borrowed)
}
/// Append matrix [`TestResultRow`]s to a workspace testmatrix sink file (the SAME
/// [`JsonFileSink`] the pane reads). The write half of "run from the matrix": a run
/// appends its rows here, then the pane re-reads them.
pub fn append_matrix_rows(path: &Path, rows: &[TestResultRow]) -> std::io::Result<()> {
use nornir_testmatrix::sink::TestSink;
JsonFileSink::new(path.to_path_buf())
.append(rows)
.map_err(|e| std::io::Error::other(e.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
/// ROBOT SCENARIO (task #66) — the **Testmatrix + Benches pane** driven over the
/// remote seam (`Panel::state_json`). Fixtures (no workspace files): assert BOTH
/// reuse consumptions land — the reused `facett_matrix::MatrixView` grid folds under
/// `matrix` (repo_count + coloured cells), and the `nornir_bench`-derived mimir trend
/// carries populate + search benches whose newer run IMPROVED. Emits a matrix cell.
#[test]
fn benches_pane_folds_matrix_and_mimir_trend_over_the_seam() {
// The grid + trend over REAL rows — built here, seeded through the SAME file
// seam the shipped pane reads (this used to run against the deleted fixtures).
let ws = "dwarves-benches-fold-ws";
let tm = workspace_testmatrix_path(ws);
let bh = workspace_bench_history_path(ws);
let _ = std::fs::remove_file(&tm);
let _ = std::fs::remove_file(&bh);
let rows: Vec<TestResultRow> = testmatrix_fixture().state().rows.clone();
append_matrix_rows(&tm, &rows).unwrap();
for run in bench_fixture() {
nornir_bench::history::append(&bh, &run).unwrap();
}
let pane = benches_panel(ws);
let st = pane.state_json();
// (1) the reused MatrixView grid is folded and painted real cells.
let matrix_ok = st["component"] == "BenchesView"
&& st["source_matrix"] == "workspace-run"
&& st.get("matrix_empty").is_none()
&& st["matrix"]["repo_count"].as_u64().unwrap_or(0) >= 3
&& st["matrix"]["total_green"].as_u64().unwrap_or(0) >= 1;
// (2) the nornir_bench-derived mimir trend: populate + search present, improving.
let benches = st["benches"].as_array().unwrap();
let has_populate = benches.iter().any(|b| b["group"] == "populate");
let has_search = benches.iter().any(|b| b["group"] == "search");
let all_improved = !benches.is_empty() && benches.iter().all(|b| b["improved"] == true);
// name parsing landed (op + size split off the mimir name).
let parsed = benches
.iter()
.any(|b| b["op"] == "symbol_facts" && b["size"] == "1k");
let bench_ok = st["source_benches"] == "workspace-run"
&& st.get("bench_empty").is_none()
&& st["bench_count"].as_u64().unwrap_or(0) >= 4
&& st["populate_count"].as_u64().unwrap_or(0) >= 1
&& st["search_count"].as_u64().unwrap_or(0) >= 1
&& st["run_count"] == 2
&& has_populate
&& has_search
&& all_improved
&& parsed;
let _ = std::fs::remove_file(&tm);
let _ = std::fs::remove_file(&bh);
let drivable = matrix_ok && bench_ok;
assert!(
drivable,
"the Testmatrix+Benches pane folds the reused grid + mimir trend: {st}"
);
facett_core::testmatrix::emit(
"dwarves-embed::benchesview",
"benches_pane_folds_matrix_and_mimir_trend",
drivable,
"Testmatrix+Benches pane: reused facett_matrix::MatrixView grid (repo×aspect cells) folded under `matrix` + nornir_bench read-API-derived nornir.mimir.<populate|search> trend (improving newest-vs-first), over rows seeded through the workspace file seam — driven headless over Panel::state_json",
);
}
/// **BOTH FIXTURES ARE GONE** (audit 2026-09-10). With no workspace files the pane
/// used to draw `testmatrix_fixture()` (a repo×aspect surface over five repos, with
/// a red unit cell and a red clippy cell) and `bench_fixture()` (two invented mimir
/// runs 1.35× apart, so **every trend row opened on a green ▲**). Pins:
///
/// (a) with no rows the grid has ZERO repos and the trend ZERO benches — and in
/// particular zero ▲, since `improved` cannot be claimed for a bench nobody ran;
/// (b) BOTH halves carry their NAMED empty state, each naming its own file;
/// (c) `source_matrix`/`source_benches` say `empty`, never `fixture`;
/// (d) none of the fixtures' invented rows survives anywhere in the state.
///
/// RED-when-broken: restore either fallback and (a)/(d) fail.
#[test]
fn an_unrun_workspace_names_both_feeds_never_a_fixture() {
let ws = "dwarves-benches-empty-ws";
let tm = workspace_testmatrix_path(ws);
let bh = workspace_bench_history_path(ws);
let _ = std::fs::remove_file(&tm);
let _ = std::fs::remove_file(&bh);
let st = benches_panel(ws).state_json();
// (a)
assert_eq!(
st["matrix"]["repo_count"], 0,
"no rows ⇒ no repos on the grid: {st}"
);
assert_eq!(st["bench_count"], 0, "…and no benches on the trend: {st}");
assert_eq!(st["run_count"], 0, "…and no runs behind it: {st}");
assert!(
st["benches"].as_array().unwrap().is_empty(),
"an invented ▲ is the exact thing this replaced: {st}"
);
// (b) + (c)
assert_eq!(
st["source_matrix"], "empty",
"`fixture` is not a source any more: {st}"
);
assert_eq!(st["source_benches"], "empty");
assert_eq!(
st["matrix_empty"]["feed"], MATRIX_FEED,
"the matrix half NAMES its file"
);
assert_eq!(st["matrix_empty"]["cause"], "no-rows");
assert_eq!(
st["bench_empty"]["feed"], BENCH_FEED,
"the bench half NAMES its file"
);
assert_eq!(st["bench_empty"]["cause"], "no-rows");
assert!(
st["bench_empty"]["detail"]
.as_str()
.unwrap()
.contains(&*bh.display().to_string()),
"…including the path it looked at: {st}"
);
// (d) the fixtures' invented rows appear NOWHERE.
let whole = st.to_string();
for gone in [
"nornir.mimir.populate.symbol_facts_1k",
"unknown-pipe",
"znippy",
"Loki",
] {
assert!(
!whole.contains(gone),
"the deleted fixtures' `{gone}` is gone: {whole}"
);
}
}
/// RED-when-broken (the GATE): a workspace with REAL testmatrix + bench files reflects
/// THOSE, not the fixtures. Seed a distinct 1-bench history + a 1-row matrix, bind, and
/// assert `source_*` flip to `workspace-run` and the pane loaded the seeded data.
#[test]
fn workspace_files_reflect_over_the_fixtures() {
let ws = "dwarves-benches-test-ws";
let tm = workspace_testmatrix_path(ws);
let bh = workspace_bench_history_path(ws);
let _ = std::fs::remove_file(&tm);
let _ = std::fs::remove_file(&bh);
// Seed a distinct matrix run (one repo, one passing unit case).
let row = TestResultRow {
run_id: "seed".into(),
repo: "mimir".into(),
suite: "mimir".into(),
test_name: "populate".into(),
status: nornir_testmatrix::status::PASS.into(),
duration_ms: 1.0,
ts_micros: 100,
message: String::new(),
aspect: "unit".into(),
metric: 0.0,
};
use nornir_testmatrix::sink::TestSink;
JsonFileSink::new(tm.clone()).append(&[row]).unwrap();
// Seed a distinct bench history: ONE mimir bench, ONE run.
let mut m = serde_json::Map::new();
m.insert("rows_per_sec".into(), serde_json::json!(555.0));
let run = BenchRun {
id: nornir_bench::BenchRun::new_id(),
date: "2026-07-18".into(),
timestamp: None,
version: "x".into(),
machine: "Loki".into(),
cores: 12,
machine_facts: None,
results: vec![nornir_bench::BenchResult::measured(
"nornir.mimir.populate.symbol_facts_9k",
m,
)],
tests: Vec::new(),
};
nornir_bench::history::append(&bh, &run).unwrap();
let pane = benches_panel(ws);
let st = pane.state_json();
let ok = st["selected_workspace"] == ws
&& st["source_matrix"] == "workspace-run"
&& st["source_benches"] == "workspace-run"
// the seeded 1-repo matrix, not the multi-repo fixture.
&& st["matrix"]["repo_count"] == 1
&& st["run_count"] == 1
&& st["bench_count"] == 1
&& st["benches"][0]["op"] == "symbol_facts"
&& st["benches"][0]["size"] == "9k"
&& st["benches"][0]["latest"] == 555.0;
assert!(
ok,
"the pane reflects the seeded workspace files, not the fixtures: {st}"
);
let _ = std::fs::remove_file(&tm);
let _ = std::fs::remove_file(&bh);
}
/// The matrix DISPLAY spans tests + **benchers** + **robot-UI** with status (the
/// separation-of-concerns deliverable: dwarves shows the full matrix). Assert the
/// grid carries `bench-smoke` + `robot-ui` aspect columns, that a robot-UI cell
/// reflects a real pass/fail verdict, and that a suite with a red scenario reds its
/// cell (never a fabricated green).
///
/// The rows are handed in DIRECTLY (2026-09-10). This used to read them off
/// `benches_panel("")`, which only worked because an unfed pane mounted
/// `testmatrix_fixture()` — so the test was reading the fallback it is not this
/// test's job to assert. What it is about is what the grid DISPLAYS given rows, so
/// it now gets rows.
#[test]
fn matrix_shows_tests_benchers_and_robot_ui_with_status() {
let m = Facet::state_json(&testmatrix_fixture());
let m = &m;
let aspects: Vec<String> = serde_json::from_value(m["aspects"].clone()).unwrap();
assert!(
aspects.iter().any(|a| a == "bench-smoke"),
"benchers column: {aspects:?}"
);
assert!(
aspects.iter().any(|a| a == "robot-ui"),
"robot-UI column: {aspects:?}"
);
assert!(
aspects.iter().any(|a| a == "unit"),
"unit tests column: {aspects:?}"
);
let rows = m["rows"].as_array().unwrap();
// dwarves' robot-UI suite has a red scenario → the robot-ui cell is red and names it.
let dwarves = rows.iter().find(|r| r["repo"] == "dwarves").unwrap();
let ru = dwarves["cells"]
.as_array()
.unwrap()
.iter()
.find(|c| c["aspect"] == "robot-ui")
.unwrap();
assert_eq!(ru["ran"], true, "dwarves robot-UI suite ran");
assert_eq!(ru["red"], true, "a failed robot-UI scenario reds the cell");
assert!(ru["red_cases"]
.as_array()
.unwrap()
.iter()
.any(|c| c["test"] == "forge_tab_drives"));
// nornir's robot-UI suite is all-green.
let nornir = rows.iter().find(|r| r["repo"] == "nornir").unwrap();
let nru = nornir["cells"]
.as_array()
.unwrap()
.iter()
.find(|c| c["aspect"] == "robot-ui")
.unwrap();
assert_eq!(nru["green"], true, "nornir robot-UI suite passed");
// benchers landed as bench-smoke greens.
let bench = dwarves["cells"]
.as_array()
.unwrap()
.iter()
.find(|c| c["aspect"] == "bench-smoke")
.unwrap();
assert_eq!(bench["green"], true);
}
/// The in-process robot-UI RUN is a genuine pane-drive: it reads each forge pane's
/// own `state_json` and yields a `robot-ui` row per pane, all green offline — each
/// pane satisfying "rows or a NAMED empty state" rather than the fixtures it used
/// to be handed.
#[test]
fn run_robot_ui_drives_the_forge_panes() {
let rows = run_robot_ui("run-1", 500);
assert!(
rows.len() >= 4,
"one robot-ui row per forge pane driven: {}",
rows.len()
);
assert!(rows
.iter()
.all(|r| r.aspect == "robot-ui" && r.repo == "dwarves"));
// Every pane satisfies its invariant → all green.
assert!(
rows.iter()
.all(|r| r.status == nornir_testmatrix::status::PASS),
"offline forge panes pass their invariants: {:?}",
rows.iter()
.map(|r| (r.test_name.clone(), r.status.clone(), r.message.clone()))
.collect::<Vec<_>>()
);
// Renamed with the check itself on 2026-09-10: it no longer asserts painted
// cells, it asserts the pane names the feed it has nothing from.
assert!(rows
.iter()
.any(|r| r.test_name == "benches_tab_names_its_empty_feeds"));
}
/// RUN-from-the-matrix over the remote seam: drive `{"run_robot_ui":true}` and assert
/// the grid gains a fresh robot-UI run for the forge (the run landed + the pane
/// re-read it), flipping the source to a matrix run.
#[test]
fn run_robot_ui_over_the_seam_refreshes_the_grid() {
let ws = "dwarves-embed-robotui-run-ws";
let tm = workspace_testmatrix_path(ws);
let _ = std::fs::remove_file(&tm);
let mut pane = BenchesView::for_workspace(ws);
// Before: no workspace file yet ⇒ the honest empty, NOT the deleted fixture.
let before = Facet::state_json(&pane);
assert_eq!(before["source_matrix"], "empty");
assert_eq!(
before["matrix"]["repo_count"], 0,
"nothing painted before the run: {before}"
);
Facet::update_json(&mut pane, r#"{"run_robot_ui":true}"#);
let st = Facet::state_json(&pane);
assert_eq!(
st["source_matrix"], "workspace-run",
"the run landed to the workspace file: {st}"
);
let rows = st["matrix"]["rows"].as_array().unwrap();
let dwarves = rows
.iter()
.find(|r| r["repo"] == "dwarves")
.expect("dwarves row present");
let ru = dwarves["cells"]
.as_array()
.unwrap()
.iter()
.find(|c| c["aspect"] == "robot-ui")
.unwrap();
assert_eq!(
ru["ran"], true,
"the robot-UI suite ran and shows in the grid: {st}"
);
assert_eq!(
ru["green"], true,
"offline forge panes all pass → green robot-UI cell"
);
let _ = std::fs::remove_file(&tm);
}
#[test]
fn mimir_name_parses_group_op_size() {
assert_eq!(
parse_mimir_name("nornir.mimir.populate.symbol_facts_1k"),
Some(("populate".into(), "symbol_facts".into(), "1k".into()))
);
assert_eq!(
parse_mimir_name("nornir.mimir.search.hybrid_rrf_100k"),
Some(("search".into(), "hybrid_rrf".into(), "100k".into()))
);
assert_eq!(parse_mimir_name("znippy.compress_lz4"), None);
}
}