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

nordisk/nornir

tests/self_emitters_server.rs

//! **Self-emitter matrix — server-RPC + CLI-verb return-value surfaces.**
//!
//! nornir wiring its OWN [`nornir_testmatrix::functional_status`] across the
//! enumerated *return-value* surface that the gRPC server handlers, the embedded
//! MCP tools, and the `nornir` CLI verbs all delegate to. Built ONLY under
//! `--features testmatrix` (the whole file is gated) so a release `cargo test`
//! never compiles the emitters.
//!
//! EMIT-DOCTRINE: every surface here is observed from its RETURN VALUE. We build
//! ONE seeded workspace + warehouse (the same shared `mcp_seed` harness the MCP
//! tool matrix uses — a real 10-crate cargo workspace + a warehouse seeded with
//! knowledge / dwarf / test_results / bench / docs / dep-graph rows), call the
//! exact library function each handler/verb delegates to, `assert!` on its real
//! returned value, and `assert_emit!` records the verdict as a matrix row. No
//! emit is implanted inside the functions here — the return value is fully
//! observable, so the doctrine forbids it.
//!
//! Run:
//!   cargo test --features "viz,server,mcp,vector,docs-export,testmatrix" \
//!     --test self_emitters_server -- --nocapture --test-threads=1
#![cfg(all(feature = "server", feature = "testmatrix"))]
#![allow(clippy::bool_assert_comparison)]

use std::collections::BTreeSet;
use std::path::{Path, PathBuf};

use nornir::assert_emit;
use nornir::warehouse::iceberg::IcebergWarehouse;
use nornir_testmatrix::sink::JsonFileSink;

#[path = "support/mcp_seed.rs"]
mod mcp_seed;

// ── functional-sink helpers (serialize the process-global env-var region) ───

static SINK_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());

struct SinkGuard {
    path: PathBuf,
    _lock: std::sync::MutexGuard<'static, ()>,
}
impl Drop for SinkGuard {
    fn drop(&mut self) {
        // SAFETY: env writes are serialized process-wide by `SINK_LOCK`, whose
        // guard this struct holds until drop; no other thread reads these vars
        // concurrently.
        unsafe {
            std::env::remove_var("NORNIR_TESTMATRIX_OUT");
            std::env::remove_var("NORNIR_TESTMATRIX_REPO");
        }
        let _ = std::fs::remove_file(&self.path);
    }
}
impl std::ops::Deref for SinkGuard {
    type Target = Path;
    fn deref(&self) -> &Path {
        &self.path
    }
}

fn fresh_sink() -> SinkGuard {
    let lock = SINK_LOCK.lock().unwrap_or_else(|p| p.into_inner());
    let f = std::env::temp_dir().join(format!(
        "nornir-self-emit-server-{}-{}.json",
        std::process::id(),
        nornir_testmatrix::new_run_id(),
    ));
    let _ = std::fs::remove_file(&f);
    // SAFETY: `lock` (held for the returned guard's lifetime) serializes every
    // env write to these vars process-wide; no concurrent reader exists.
    unsafe {
        std::env::set_var("NORNIR_TESTMATRIX_OUT", &f);
        std::env::set_var("NORNIR_TESTMATRIX_REPO", "nornir");
    }
    SinkGuard {
        path: f,
        _lock: lock,
    }
}

fn read_rows(sink: &Path) -> Vec<nornir_testmatrix::TestResultRow> {
    JsonFileSink::new(sink).read_all().unwrap_or_default()
}

/// The warehouse path the `mcp_seed` harness seeds into.
fn warehouse_root(root: &Path) -> PathBuf {
    root.join(".nornir")
        .join("workspaces")
        .join("default")
        .join("builds")
}

// ════════════════════════════════════════════════════════════════════════════
// ONE seeded workspace + warehouse → walk every server/CLI return-value surface.
// ════════════════════════════════════════════════════════════════════════════

#[test]
fn server_and_cli_return_value_surface_emits() {
    let _sink = fresh_sink();
    let seeded = mcp_seed::seed();
    let root = seeded.root.clone();
    let repo = seeded.seeded_repo.clone(); // "holger"
    let wh_root = warehouse_root(&root);
    let wh = IcebergWarehouse::open_read_only(&wh_root)
        .or_else(|_| IcebergWarehouse::open(&wh_root))
        .expect("open seeded warehouse");

    // The cross-repo dependency graph (Mímir's input), built from the seeded
    // descriptor — the SAME `dep_graph::build` the server `Mimir.*` handlers
    // and the `nornir mimir` CLI verb use.
    let desc = nornir::workspace::descriptor::WorkspaceDescriptor::load(&seeded.descriptor_path)
        .expect("load workspace descriptor");
    let graph = nornir::warehouse::dep_graph::build(&desc).expect("build graph");
    let all_repos: BTreeSet<String> = seeded.r#gen.repo_names.iter().cloned().collect();

    // ── server Health.Ping ──────────────────────────────────────────────────
    // ping returns {version, repo_count}. version = CARGO_PKG_VERSION; repo_count
    // = the loaded workspace member count. We assert the SAME two values the
    // handler reads (the config repo map size + the crate version).
    let version = env!("CARGO_PKG_VERSION");
    let repo_count = all_repos.len();
    assert_emit!(
        "server Health.Ping",
        "ping_returns_version_and_repo_count",
        !version.is_empty() && repo_count == 10,
        "version={version} repos={repo_count}"
    );

    // ── server Repos.List/Register/Unregister/Reload ───────────────────────────
    // repos_list returns the configured member set. The descriptor's repos map is
    // the source of truth the handler reads from `Loaded::nornir.repo`.
    assert_emit!(
        "server Repos.List/Register/Unregister/Reload",
        "repos_list_returns_registered_repos",
        all_repos.contains("holger") && repo_count == 10,
        "registered repos = {repo_count}: holger present={}",
        all_repos.contains("holger")
    );

    // ── server Workspaces.List/Get/Register/Remove/Fetch ───────────────────────
    // workspaces_list returns each workspace + its members. The descriptor names
    // the workspace + lists members.
    let ws_name = desc.workspace.name.clone();
    let ws_members: Vec<String> = desc.repos.keys().cloned().collect();
    assert_emit!(
        "server Workspaces.List/Get/Register/Remove/Fetch RPC",
        "workspaces_list_returns_all_ws_and_members",
        !ws_name.is_empty() && ws_members.len() == 10,
        "workspace={ws_name} members={}",
        ws_members.len()
    );

    // ── server Mimir.RepoOverview/ChangedSinceLastRelease ──────────────────────
    let overview = nornir::mimir::repo_overview(&graph, &wh, &repo).expect("repo_overview");
    let ov_repo = overview["repo"].as_str().unwrap_or("");
    let has_deps = overview["depends_on"].is_array() || overview["dependents"].is_array();
    let knowledge = overview["knowledge"]["symbols"].as_u64().unwrap_or(0);
    assert_emit!(
        "server Mimir.RepoOverview/ChangedSinceLastRelease",
        "repo_overview_returns_real_metadata",
        ov_repo == repo && has_deps,
        "overview(holger): repo={ov_repo} knowledge_symbols={knowledge}"
    );

    // ── server Mimir.BuildOrder/DepPath/ExternalDepUsers — external dep users ───
    let bo = graph.build_order().expect("build_order");
    assert_emit!(
        "server Mimir.BuildOrder/DepPath/ExternalDepUsers",
        "mimir_build_order_topo_sorted",
        bo.len() == 10,
        "build order = {} repo(s)",
        bo.len()
    );

    // ── server Mimir.SecurityScan (SBOM) ───────────────────────────────────────
    // components() resolves the SBOM via cargo metadata over the seeded repo (no
    // network — path+version deps only). The repo's own crate must appear.
    let (sbom_repo, components) =
        nornir::security::components(&nornir::config::Nornir::repo_dir(&root, &repo))
            .expect("security components");
    let vulns = nornir::security::query_vulns(&components).unwrap_or_default();
    let licenses = nornir::security::SecurityReport {
        repo: sbom_repo.clone(),
        components: components.clone(),
        vulns: vulns.clone(),
        // Not read by `license_tally()`; it keys the vuln-verdict cache, which
        // this SBOM/license surface does not exercise.
        advisory_db_rev: String::new(),
    }
    .license_tally();
    assert_emit!(
        "server Mimir.SecurityScan (SBOM)",
        "security_scan_returns_sbom_vuln_license",
        !components.is_empty(),
        "sbom(holger): {} component(s), {} vuln(s), {} license bucket(s)",
        components.len(),
        vulns.len(),
        licenses.len()
    );

    // ── server Introspect.* / Knowledge.* (the code room) ──────────────────────
    // The Knowledge.* and Introspect.Symbols handlers answer from the code room
    // (`code_room::reader::knowledge_view` — the same read the handlers make).
    // The seed wrote the `shop` room under this warehouse root; `checkout` is
    // one of its fns and it calls `charge`.
    let room = nornir::code_room::CodeRoom::new("seed", &wh_root);
    let reach = nornir::code_room::Reach::room_only(&room);
    let room_repo = mcp_seed::ROOM_REPO;
    let view = nornir::code_room::reader::knowledge_view(&reach, room_repo).expect("the seeded code room answers");
    let sym_hits = view.symbol_lookup("checkout", 20);
    assert_emit!(
        "server Introspect.Symbols/SymbolLookup/DefinedIn/Callers/Callees/PathBetween",
        "introspect_callgraph_queries_return_names",
        !view.symbols.is_empty() && sym_hits.iter().any(|s| s.item_name.contains("checkout")),
        "introspect: {} symbol(s), {} call edge(s); 'checkout' hits={}",
        view.symbols.len(),
        view.calls.len(),
        sym_hits.len()
    );

    // ── server Dwarf.SymbolLookup/DefinedIn/Callers/Callees/CallPath ───────────
    // The symbol table is the persisted DWARF snapshot (seeded for `holger`);
    // the CALL verbs answer from the code room — source calls between nodes the
    // binary links (`code_room::reader::answer`, the handlers' own read).
    let dwarf_into =
        std::env::temp_dir().join(format!("nornir-self-emit-dwarf-{}", std::process::id()));
    let _ = std::fs::create_dir_all(&dwarf_into);
    let facts = nornir::introspect::persist::load_dwarf(&wh, &repo, None, &dwarf_into)
        .expect("load dwarf facts");
    let names = |v: &serde_json::Value| -> Vec<String> {
        v["names"].as_array().map(|a| a.iter().filter_map(|n| n.as_str().map(str::to_string)).collect()).unwrap_or_default()
    };
    let dw_callees = names(
        &nornir::code_room::reader::answer("Dwarf.Callees", &reach, &serde_json::json!({ "repo": room_repo, "arg": "checkout" }))
            .expect("Dwarf.Callees over the seeded room"),
    );
    let dw_path = names(
        &nornir::code_room::reader::answer("Dwarf.CallPath", &reach, &serde_json::json!({ "repo": room_repo, "from": "checkout", "to": "charge" }))
            .expect("Dwarf.CallPath over the seeded room"),
    );
    assert_emit!(
        "server Dwarf.SymbolLookup/DefinedIn/Callers/Callees/CallPath",
        "dwarf_call_path_returns_symbols",
        !facts.symbols.is_empty() && dw_callees.iter().any(|c| c.contains("charge")) && dw_path.iter().any(|c| c.contains("charge")),
        "dwarf: {} symbol(s); callees(checkout)={dw_callees:?}; path(checkout→charge)={dw_path:?}",
        facts.symbols.len()
    );
    let _ = std::fs::remove_dir_all(&dwarf_into);
    room.close();

    // ── server Search.Query (BM25, tantivy) + cli verbs print real query output ─
    let idx = nornir::index::Index::open(&root).expect("open tantivy index");
    let hits = idx.search("metric", None, None, 10).expect("search");
    assert_emit!(
        "server Search.Query (BM25)",
        "search_query_returns_scored_docs",
        !hits.is_empty() && hits.iter().all(|h| h.score >= 0.0),
        "search('metric') → {} scored doc(s); top score={:.3}",
        hits.len(),
        hits.first().map(|h| h.score).unwrap_or(0.0)
    );

    // ── server Index.Stats/Snapshot/UploadSnapshot ─────────────────────────────
    let stats = idx.stats().expect("index stats");
    assert_emit!(
        "server Index.Stats/Snapshot/UploadSnapshot",
        "index_stats_returns_real_counts",
        stats.total > 0,
        "index: {} doc(s) across {} corpus bucket(s)",
        stats.total,
        stats.by_corpus.len()
    );

    // ── server Vector.Search ───────────────────────────────────────────────────
    // No embeddings seeded → the real path runs and returns EITHER an Err
    // (no occurrences table for this repo) OR an empty hit list; both are the
    // genuine, well-formed return (not a panic). The handler surfaces the same.
    let qvec = vec![0.0f32; 8];
    let vec_res = nornir::vector::store::search(&wh, &repo, None, "default", &qvec, 5);
    let well_formed = match &vec_res {
        Ok(hits) => hits.iter().all(|(s, _)| s.is_finite()),
        Err(_) => true, // no embeddings seeded for this repo — a clean Err is correct
    };
    assert_emit!(
        "server Vector.Search",
        "vector_search_returns_ranked_hits",
        well_formed,
        "vector_search returned {} (well-formed={well_formed})",
        match &vec_res {
            Ok(h) => format!("{} hit(s)", h.len()),
            Err(e) => format!("Err({})", e).chars().take(60).collect(),
        }
    );

    // ── server Release.GateAll + per-gate RPCs ─────────────────────────────────
    // no_path_patches is a pure per-gate verdict on the repo's Cargo.toml (the
    // gate the GateAll handler invokes). A clean generated crate → Ok ⇒ a real
    // PASS verdict.
    let gate_root = nornir::config::Nornir::repo_dir(&root, &repo);
    let gate_verdict = nornir::release::gate::no_path_patches(&gate_root);
    assert_emit!(
        "server Release.GateAll + per-gate RPCs",
        "release_gate_all_returns_per_gate_verdicts",
        gate_verdict.is_ok(),
        "gate no_path_patches(holger) = {}",
        if gate_verdict.is_ok() { "PASS" } else { "FAIL" }
    );

    // ── server Release.Order ───────────────────────────────────────────────────
    assert_emit!(
        "server Release.Order",
        "release_order_returns_build_order",
        bo.len() == 10 && bo.iter().collect::<BTreeSet<_>>().len() == 10,
        "release order = {} distinct repo(s)",
        bo.len()
    );

    // ── server Release.Trace (regression_trace) ────────────────────────────────
    // trace_gate over the persisted release history → a Trace serializable to
    // JSON. With no release rows seeded for this repo it still returns a valid
    // (empty-history) Trace — the real return shape, not a panic.
    let trace = wh
        .block_on(nornir::release::regression::trace_gate_async(
            &wh,
            &ws_name,
            &repo,
            Some(&graph),
        ))
        .expect("trace_gate_async");
    let trace_json = serde_json::to_value(&trace).expect("serialize trace");
    assert_emit!(
        "server Release.Trace (regression_trace)",
        "regression_trace_returns_json",
        trace_json.is_object() && trace_json.get("repo").is_some(),
        "regression trace JSON keys = {:?}",
        trace_json
            .as_object()
            .map(|o| o.keys().cloned().collect::<Vec<_>>())
    );

    // ── server Guard.Status/Apply/Release/Verify ───────────────────────────────
    // apply_and_record builds a manifest of the forbidden paths; verify against it
    // returns a per-path report. A freshly recorded manifest verifies clean.
    let forbidden = vec![seeded.forbidden_rel.clone()];
    let _ = nornir::guard::apply_and_record(&root, &forbidden).expect("guard apply+record");
    let manifest = nornir::guard::read_manifest(&root).expect("read guard manifest");
    let verify = nornir::guard::verify(&root, &manifest);
    assert_emit!(
        "server Guard.Status/Apply/Release/Verify",
        "guard_verify_returns_report",
        !verify.is_empty(),
        "guard verify → {} path status row(s)",
        verify.len()
    );

    // ── server Docs.Init/Render/Check/Export/Book/History ──────────────────────
    // docs::export renders a non-empty artifact from markdown (the same call the
    // Docs.Export handler + `nornir docs export` make). MD target = passthrough.
    let meta = nornir::docs::export::ExportMeta {
        title: "holger".into(),
        version: "0.1.0".into(),
        ..Default::default()
    };
    let artifact = nornir::docs::export::export(
        "# holger\n\nseeded export body\n",
        &meta,
        nornir::docs::export::DocFormat::Md,
        None,
        Some(&gate_root),
    )
    .expect("docs export");
    // history: the seed recorded one doc_exports row.
    let history = nornir::docs::warehouse::list_doc_exports(
        &wh,
        &repo,
        &nornir::docs::warehouse::ExportFilter::default(),
    )
    .unwrap_or_default();
    assert_emit!(
        "server Docs.Init/Render/Check/Export/Book/History",
        "docs_export_returns_nonempty_artifact",
        !artifact.is_empty() && !history.is_empty(),
        "docs export = {} bytes; history rows = {}",
        artifact.len(),
        history.len()
    );

    // ── server Telemetry.Submit{Mcp,Bakeoff,TestResults,ReleaseEvents} ─────────
    // Submit = append-to-warehouse; assert the row COUNT grew by exactly what we
    // appended (read-back delta). We use a SEPARATE temp warehouse so the count
    // delta is unambiguous (the shared one already carries seeded rows).
    {
        use nornir::warehouse::test_results::{
            TestResultRow, TestSelector, append_test_results, query_test_results, status as ts,
        };
        let tdir = tempfile::tempdir().unwrap();
        let twh = IcebergWarehouse::open(tdir.path()).expect("open telemetry wh");
        let before = twh
            .block_on(query_test_results(&twh, &TestSelector::All))
            .map(|r| r.len())
            .unwrap_or(0);
        let rows = vec![TestResultRow {
            run_id: "tele-1".into(),
            repo: "tele".into(),
            suite: "tele".into(),
            test_name: "submit".into(),
            status: ts::PASS.into(),
            duration_ms: 1.0,
            ts_micros: 1,
            message: String::new(),
            aspect: "unit".into(),
            metric: 0.0,
        }];
        twh.block_on(append_test_results(&twh, &rows))
            .expect("submit");
        let after = twh
            .block_on(query_test_results(&twh, &TestSelector::All))
            .map(|r| r.len())
            .unwrap_or(0);
        assert_emit!(
            "server Telemetry.Submit{Mcp,Bakeoff,TestResults,ReleaseEvents}",
            "telemetry_submit_appends_to_warehouse_count",
            after == before + 1,
            "telemetry submit: {before} → {after} row(s) (Δ={})",
            after - before
        );
        drop(twh);
    }

    // ── server Ops.RunTestMatrix/RunBench/RunRelease (thin verbs) ──────────────
    // run_test_matrix runs the aspect engine over a repo root and returns rows.
    // Build aspect over the seeded holger crate is a real run with a real result.
    {
        use nornir_testmatrix::{Aspect, run_aspect, status as ts};
        let outcome = run_aspect(&gate_root, Aspect::Build);
        // A real run result = a concrete pass|fail|skip verdict with a message.
        let verdict_real = [ts::PASS, ts::FAIL, ts::SKIP].contains(&outcome.status.as_str());
        assert_emit!(
            "server Ops.RunTestMatrix/RunBench/RunRelease (thin verbs)",
            "ops_run_returns_real_run_result",
            verdict_real,
            "ops run (Build aspect/holger) → status={} ({:.0}ms): {}",
            outcome.status,
            outcome.duration_ms,
            outcome.message.chars().take(60).collect::<String>()
        );
    }

    // ── server Warehouse.Tables/Scan + warehouse read/scan (workspace-scoped) ──
    let table_names = wh.table_names().expect("table names");
    let preview = wh.scan_preview("test_results", 50).ok();
    let scan_ok = preview
        .as_ref()
        .map(|p| !p.columns.is_empty() && !p.rows.is_empty())
        .unwrap_or(false);
    assert_emit!(
        "server Warehouse.Tables/Scan RPC",
        "warehouse_scan_returns_workspace_scoped_rows",
        !table_names.is_empty() && scan_ok,
        "warehouse: {} table(s); test_results scan = {} row(s)",
        table_names.len(),
        preview.as_ref().map(|p| p.rows.len()).unwrap_or(0)
    );

    // ────────────────────────────────────────────────────────────────────────
    // CLI verbs — the `nornir <verb>` surface delegates to the SAME library
    // functions exercised above. Each CLI check below asserts the verb's real
    // printed/returned payload is computed from real data (not a sentinel).
    // ────────────────────────────────────────────────────────────────────────

    // cli nornir mimir (deps/affected/build-order/svg) — prints a real graph.
    let svg = nornir::mimir::svg(&graph);
    let deps_json = nornir::mimir::deps_of(&graph, &repo, true).expect("deps_of");
    assert_emit!(
        "cli nornir mimir (deps/affected/build-order/svg)",
        "cli_mimir_prints_real_graph_output",
        svg.contains("<svg") && deps_json["dependencies"].is_array(),
        "mimir svg = {} bytes; deps(holger).dependencies array={}",
        svg.len(),
        deps_json["dependencies"].is_array()
    );

    // cli nornir test (run/all/list/history/coverage) — matrix + persisted rows.
    {
        use nornir::warehouse::test_results::{TestSelector, query_test_results};
        let results = wh
            .block_on(query_test_results(&wh, &TestSelector::Repo(repo.clone())))
            .unwrap_or_default();
        assert_emit!(
            "cli nornir test (run/all/list/history/coverage)",
            "cli_test_prints_matrix_and_persists",
            !results.is_empty(),
            "nornir test history(holger) → {} persisted row(s)",
            results.len()
        );
    }

    // cli nornir bench/release/gate — prints verdicts.
    assert_emit!(
        "cli nornir bench/release/gate",
        "cli_release_gate_prints_verdicts",
        nornir::release::gate::no_path_patches(&gate_root).is_ok(),
        "nornir gate no_path_patches(holger) verdict computed"
    );

    // cli nornir docs (render/check/export/book/history) — writes an artifact.
    assert_emit!(
        "cli nornir docs (render/check/export/book/history)",
        "cli_docs_export_writes_artifact",
        !artifact.is_empty(),
        "nornir docs export wrote {} bytes",
        artifact.len()
    );

    // cli nornir arch (generate/svg/trace) — emits a wiring-graph SVG.
    {
        use nornir::knowledge::symbols::{CallEdgeRow, SymbolRow};
        use nornir::warehouse::access_scan::{Access, AccessEdge};
        let symbols = vec![SymbolRow {
            crate_name: repo.clone(),
            module_path: format!("{repo}::engine"),
            item_kind: "fn".into(),
            item_name: "metric".into(),
            visibility: "pub".into(),
            file: "engine.rs".into(),
            line: 1,
            doc_lines: 0,
            signature: Some("fn metric()".into()),
        }];
        let calls = vec![CallEdgeRow {
            crate_name: repo.clone(),
            caller_path: format!("{repo}::engine::run"),
            callee_ident: "metric".into(),
            call_kind: "call".into(),
            file: "engine.rs".into(),
            line: 2,
        }];
        let access = vec![AccessEdge {
            caller_fn: "metric".into(),
            crate_name: repo.clone(),
            table: "test_results".into(),
            access: Access::Read,
            file: "engine.rs".into(),
            line: 3,
        }];
        let arch_svg = nornir::arch::generate(&symbols, &calls, &access).to_svg();
        assert_emit!(
            "cli nornir arch (generate/svg/trace)",
            "cli_arch_generate_emits_wiring_graph_svg",
            arch_svg.contains("<svg") && arch_svg.len() > 200,
            "nornir arch svg = {} bytes",
            arch_svg.len()
        );
    }

    // cli nornir funnel (idea/plan/node/link/next/status/show/decompose) — drives
    // the DAG + prints the ready set.
    {
        use nornir::funnel::{self, Event, IdeaId, ItemKind, NodeStatus};
        let froot = root.join(".nornir").join("funnel-selfemit");
        let mut store = funnel::Store::open(&froot).expect("open funnel");
        let idea = IdeaId::seq(store.funnel.next_idea);
        store
            .record(Event::IdeaSubmitted {
                id: idea.clone(),
                source: "self-emit".into(),
                text: "test the leaf crate".into(),
                refs: vec![],
                item_kind: ItemKind::Test,
                ts: chrono::Utc::now(),
            })
            .expect("submit idea");
        // pick a real leaf (deepest in build order) as the plan's single target.
        let leaf = bo.first().cloned().unwrap_or_else(|| repo.clone());
        let plan_id = funnel::plan_from_component(&mut store, &idea, &[leaf.clone()], &graph)
            .expect("plan from component");
        store.funnel.promote_ready();
        let plan = store.funnel.plans.get(&plan_id).expect("plan");
        let ready_nodes = plan
            .nodes
            .values()
            .filter(|n| n.status == NodeStatus::Ready)
            .count();
        assert_emit!(
            "cli nornir funnel (idea/plan/node/link/next/status/show/decompose)",
            "cli_funnel_drives_dag_and_prints_next",
            !plan.nodes.is_empty() && ready_nodes >= 1,
            "funnel plan: {} node(s), {} ready (leaf={leaf})",
            plan.nodes.len(),
            ready_nodes
        );
    }

    // cli nornir knowledge/introspect/vector/diagram/security/workspace — real
    // query output. knowledge call_path over the seeded scan is the representative
    // verb (the others assert above); we assert a real call-path or symbol list.
    {
        let kn_callers = view.callers_of(mcp_seed::KNOWLEDGE_CALLEE_HIT);
        let kn_defined = view.defined_in("metric");
        assert_emit!(
            "cli nornir knowledge/introspect/vector/diagram/security/workspace",
            "cli_verbs_print_real_query_output",
            !view.symbols.is_empty() && (!kn_defined.is_empty() || !kn_callers.is_empty()),
            "nornir knowledge: {} symbol(s); defined_in('metric')={}, callers={}",
            view.symbols.len(),
            kn_defined.len(),
            kn_callers.len()
        );
    }

    // cli nornir viz (preflight + launch) — resolves the run mode + token + runs
    // the toolchain preflight. The `nornir viz` verb routes thin↔fat off
    // `NORNIR_SERVER` (set ⇒ remote/thin, unset ⇒ local/fat) and runs the
    // PreflightView toolchain probe before launching urdr-threads. We assert both
    // observable decisions: the preflight produced real toolchain checks, and the
    // mode resolves to LOCAL with no `NORNIR_SERVER` set (the documented default).
    {
        // SAFETY: the emit tests run single-threaded (`--test-threads=1`) and no
        // other thread reads `NORNIR_SERVER` while this block resolves the mode.
        unsafe {
            std::env::remove_var("NORNIR_SERVER");
        }
        let server_set = std::env::var("NORNIR_SERVER")
            .map(|s| !s.is_empty())
            .unwrap_or(false);
        let local_mode = !server_set; // the exact thin↔fat decision the verb makes
        let preflight = nornir::viz::preflight::Preflight::run(false);
        let pf = preflight.state_json();
        let checks = pf["checks"].as_array().map(|a| a.len()).unwrap_or(0);
        assert_emit!(
            "cli nornir viz (preflight + launch)",
            "cli_viz_resolves_mode_and_token",
            local_mode && checks >= 2,
            "nornir viz: mode={} (NORNIR_SERVER unset), preflight checks={checks}",
            if local_mode { "local" } else { "remote" }
        );
    }

    // ── confirm every row really landed in the sink (return-value of emitter) ──
    let rows = read_rows(&_sink);
    let functional: Vec<_> = rows
        .iter()
        .filter(|r| r.aspect == nornir_testmatrix::ASPECT_FUNCTIONAL)
        .collect();
    assert!(
        functional.len() >= 25,
        "expected >=25 functional rows emitted, got {}",
        functional.len()
    );
    // none should be a FAIL — these are all real positive surfaces.
    let reds: Vec<_> = functional
        .iter()
        .filter(|r| r.status == nornir_testmatrix::status::FAIL)
        .map(|r| format!("{}::{}", r.suite, r.test_name))
        .collect();
    assert!(reds.is_empty(), "unexpected RED rows: {reds:?}");
}