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

nordisk/dwarves

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