nordisk/dwarves
dwarves-gui/src/mimir_lens.rs
//! **The 🔍 Lenses tab — the six Mimir lenses.**
//!
//! The UI surface of the Mimir lenses: the generic bloat tax, zombie cargo features,
//! change-risk scoring, weak (cannot-go-red) tests, un-benched hot paths, and the
//! relocated similar-code query. One pane with six views rather than six tabs, because
//! they are the same shape — *a ranked list of findings with its evidence beside it* —
//! and the shell already carries 20 tabs. Six near-identical tabs would be a twin of
//! the same renderer six times (L5), which is the thing this constellation keeps
//! deleting.
//!
//! The QUERY half of every lens now lives in **nornir** as the `Mimir.{GenericCost,
//! ZombieFeatures,ChangeRisk,SimilarCode,WeakTests,BenchGaps}` RPCs (L5a) — so korp's
//! UI and nornir's own viz can reach them too. This pane reads nornir's warehouse over
//! the thin `Warehouse.Scan` gRPC seam and folds it with the IDENTICAL engine code the
//! RPCs run (the fold was lifted into nornir verbatim), so the tab and the RPC can
//! never disagree about what a finding is. Re-pointing this pane's reads to call the
//! `Mimir.*` RPCs directly is a follow-up that needs a trimmed `mimir.proto` in
//! `nornir-warehouse-embed` (the drift-guarded stub crate) — the numbers are already
//! identical either way.
//!
//! The pane owns **no query logic**. Every finding comes from the `dwarves::mimir`
//! engine, which `dwarves mimir {bloat,zombie,risk}` drives through the identical calls,
//! so the tab and the command line can never disagree about what a finding is.
//!
//! ## ARCHITECTURE (L5a) — surfaced IN THE UI, not just in a doc comment
//! The QUERY half of each lens belongs in **nornir**, which owns the warehouse, exposed
//! as an RPC so korp's UI and nornir's own viz can use it too. What belongs in dwarves is
//! the GATE and the PROPOSAL. The pane renders
//! [`dwarves::mimir::PROPOSED_RPCS`] verbatim in its **where this belongs** view, so the
//! staging decision is visible to whoever opens the tab rather than buried in a module
//! doc nobody reads.
//!
//! ## What the pane must never do
//! **Show a composite score alone.** The risk view prints `callers`, `churn` and
//! `coverage` as their own columns beside the score, and carries Tornhill's citation on
//! screen — a number nobody can trace is the failure this constellation keeps retracting.
//!
//! **Render an empty table as a clean result.** Every view leads with the engine's
//! `evidence_note`, which names MISSING FEEDS by table; today that means
//! `scip_occurrences`, `coverage_fn` and `semver_diffs` are called out on sight.
use std::collections::BTreeMap;
use dwarves::coverage::CoverageIndex;
use dwarves::mimir::{bloat, collide, risk, zombie, Sources, PROPOSED_RPCS};
use facett_core::Facet;
/// Row cap per table on the thin read. `symbol_sizes` is the big one (1.35M rows for a
/// dozen analysed artifacts).
const SCAN_LIMIT: u32 = 2_000_000;
/// Where the pane's warehouse rows come from. Injected so the robot test drives the
/// pane over a deterministic fixture with no server.
pub trait LensSource: Send {
fn load(&self) -> Result<Sources, String>;
fn describe(&self) -> String;
/// Manifest facts per repo (declared vs built features) — needed by the zombie lens.
fn manifests(&self) -> BTreeMap<String, zombie::ManifestFacts> {
BTreeMap::new()
}
/// **The THIN answer.** A remote source folds on the SERVER: it calls nornir's six
/// `Mimir.*` lens RPCs and returns each lens's ranked findings + evidence_note as
/// the RPC produced them (the report's `state_json`). `None` (the default) means
/// this source supplies RAW warehouse `Sources` via [`load`](Self::load) and the
/// pane folds them LOCALLY — the fat/embedded path. `Some` short-circuits the local
/// fold: the pane renders exactly what the server computed.
fn remote(&self) -> Option<Result<RemoteReports, String>> {
None
}
/// **Does this source fold on the server?** A question about the source, answered
/// without asking anything: [`remote`](Self::remote) runs every lens RPC in series
/// and must never be called to find this out (2026-10-14: the pane called it from
/// `error_code`, i.e. from every `state_json`, i.e. every frame while an error was
/// held — on the real workspace `world_impact` takes minutes and the window froze).
fn folds_on_the_server(&self) -> bool {
false
}
/// **The same remote fold, OFF the caller's thread, ONE LENS AT A TIME.** A
/// source that folds on the server returns a receiver of per-lens updates the
/// pane polls once per frame: every lens is asked on its own worker, each answer
/// lands as it comes, and a lens the server answers `queued` / `running` (a jera
/// job) is asked again until it lands — so no lens waits for, or is blanked by,
/// another (2026-10-16: one failed RPC or the 20 s client bound blanked the whole
/// tab). The channel closes when every lens has its last answer. `None` = this
/// source has no remote fold (the local-fold sources).
fn remote_spawn(&self) -> Option<std::sync::mpsc::Receiver<LensUpdate>> {
None
}
/// **Set the Churn frequency-filter params** the change/anomaly view drives
/// (band = high/low/band, and the low/high cutoffs). Default no-op — only a remote
/// source (which serves `Mimir.Churn`) honours it.
fn set_churn_params(&mut self, _band: &str, _cutoff_low: i64, _cutoff_high: i64) {}
/// **Set the subclone repo scope** — the ONE repo `Mimir.Subclone` folds
/// (the lens is per-repo by construction; `""` asks for its refusal, which
/// names the askable repos). Default no-op — only a remote source honours it,
/// exactly like the Churn params above.
fn set_subclone_repo(&mut self, _repo: &str) {}
/// **Set the group_twins repo scope** — the second SOURCE-door lens is per-repo
/// by construction too (`""` asks for its refusal naming the askable repos).
/// Its OWN knob, not subclone's: a click in one lens never re-scopes the other.
fn set_group_twins_repo(&mut self, _repo: &str) {}
/// **Re-ask ONE lens, off the caller's thread** — the same per-lens worker
/// [`remote_spawn`](Self::remote_spawn) uses, for a single lens (what a
/// subclone repo click drives: one re-ask, never a whole re-scan). `None` =
/// this source has no remote fold.
fn remote_spawn_one(
&self,
_lens: &'static str,
) -> Option<std::sync::mpsc::Receiver<LensUpdate>> {
None
}
}
/// The shipped source: nornir's warehouse over the thin gRPC read seam.
pub struct WarehouseLens {
pub endpoint: String,
pub token: String,
pub workspace: String,
}
impl WarehouseLens {
pub fn new(
endpoint: impl Into<String>,
token: impl Into<String>,
workspace: impl Into<String>,
) -> Self {
Self {
endpoint: endpoint.into(),
token: token.into(),
workspace: workspace.into(),
}
}
}
impl LensSource for WarehouseLens {
fn load(&self) -> Result<Sources, String> {
let mut tables = BTreeMap::new();
let mut first_err = None;
for t in dwarves::mimir::TABLES
.iter()
.chain(dwarves::coverage::TABLES.iter())
{
// A table nothing writes since Mímir v2 is not asked for: it is an empty
// feed here, and the folds' evidence names it as NOT READ.
if dwarves::retired::is_retired(t) {
tables.insert((*t).to_string(), Default::default());
continue;
}
match nornir_warehouse_embed::scan_warehouse_table(
&self.endpoint,
&self.token,
&self.workspace,
t,
SCAN_LIMIT,
) {
Ok(s) => {
tables.insert(
(*t).to_string(),
dwarves::coverage::rows::Table::new(s.columns, s.rows),
);
}
Err(e) => {
first_err.get_or_insert(e.to_string());
tables.insert((*t).to_string(), Default::default());
}
}
}
// A server that answered nothing at all is UNREACHABLE, not an empty warehouse
// — two different diagnoses, and conflating them is how an outage reads as a
// clean bill of health.
if tables
.values()
.all(|t: &dwarves::coverage::rows::Table| t.is_empty())
{
if let Some(e) = first_err {
return Err(e);
}
}
let mut s = Sources::default();
s.add_root(self.describe(), tables);
Ok(s)
}
fn describe(&self) -> String {
format!("nornir warehouse @ {} / {}", self.endpoint, self.workspace)
}
}
/// The never-blank demo source. Its shape is the real 2026-07-22 reading: one artifact
/// whose tax is mostly unactionable glue plus one named generic that IS actionable, one
/// feature declared-and-never-built, one repo whose build space is unknown, and a
/// churning uncovered file.
#[cfg(any(test, feature = "fixtures"))]
pub struct FixtureLens;
#[cfg(any(test, feature = "fixtures"))]
impl FixtureLens {
fn t(cols: &[&str], rows: &[&[&str]]) -> dwarves::coverage::rows::Table {
dwarves::coverage::rows::Table::new(
cols.iter().map(|s| s.to_string()).collect(),
rows.iter()
.map(|r| r.iter().map(|s| s.to_string()).collect())
.collect(),
)
}
}
#[cfg(any(test, feature = "fixtures"))]
impl LensSource for FixtureLens {
fn load(&self) -> Result<Sources, String> {
let mut tables = BTreeMap::new();
tables.insert(
"binary_facts".to_string(),
Self::t(
&["content_sha256", "repo", "artifact_path", "total_bytes"],
&[&["c1", "znippy", "/t/release/znippy", "1000000"]],
),
);
tables.insert(
"symbol_sizes".to_string(),
Self::t(
&[
"content_sha256",
"crate_name",
"symbol_demangled_no_generics",
"symbol_demangled",
"size_bytes",
"kind",
"is_monomorphization",
],
&[
&[
"c1",
"core",
"core::ptr::drop_in_place",
"core::ptr::drop_in_place::<A>",
"90000",
"Text",
"true",
],
&[
"c1",
"core",
"core::ptr::drop_in_place",
"core::ptr::drop_in_place::<B>",
"90000",
"Text",
"true",
],
&[
"c1",
"znippy",
"znippy::parse_stream",
"znippy::parse_stream::<u8>",
"20000",
"Text",
"true",
],
&[
"c1",
"znippy",
"znippy::parse_stream",
"znippy::parse_stream::<u16>",
"20000",
"Text",
"true",
],
&[
"c1",
"znippy",
"znippy::parse_stream",
"znippy::parse_stream::<u32>",
"20000",
"Text",
"true",
],
],
),
);
tables.insert(
"feature_gate_facts".to_string(),
Self::t(
&[
"repo",
"crate_name",
"module_path",
"item_name",
"cfg_expr",
"file",
"line",
],
&[
&[
"korp",
"korp",
"korp::spark",
"spark_ingest",
"feature = \"spark41\"",
"src/spark.rs",
"12",
],
&[
"knut",
"knut",
"knut::g",
"gated",
"feature = \"grpc\"",
"src/g.rs",
"3",
],
],
),
);
tables.insert(
"call_edges".to_string(),
Self::t(
&[
"snapshot_id",
"repo",
"caller_path",
"callee_ident",
"call_kind",
],
&[
&["s1", "znippy", "znippy::tests::t", "parse_stream", "call"],
&["s1", "znippy", "znippy::hot::drive", "parse_stream", "call"],
&["s2", "knut", "knut::x", "gated", "call"],
],
),
);
tables.insert(
"symbol_facts".to_string(),
Self::t(
&[
"snapshot_id",
"ts_micros",
"repo",
"crate_name",
"module_path",
"item_kind",
"item_name",
"visibility",
"file",
"line",
],
&[
&[
"s1",
"100",
"znippy",
"znippy",
"znippy::hot",
"fn",
"parse_stream",
"pub",
"src/hot.rs",
"10",
],
// A CROSS-REPO name collision: two repos each hand-roll `nornir_escape::esc::escape`,
// so the call graph (which binds by name) cannot tell whose it is — the graph-soundness
// hazard the name-collision lens surfaces.
&[
"s2",
"200",
"korp",
"nornir_escape",
"esc",
"fn",
"escape",
"pub",
"src/esc.rs",
"5",
],
&[
"s2",
"200",
"knut",
"nornir_escape",
"esc",
"fn",
"escape",
"pub",
"src/esc.rs",
"9",
],
],
),
);
tables.insert(
"git_heat_facts".to_string(),
Self::t(
&["repo", "file", "commits_90d"],
&[&["znippy", "src/hot.rs", "37"]],
),
);
let mut s = Sources::default();
s.add_root(self.describe(), tables);
Ok(s)
}
fn describe(&self) -> String {
"fixture (demo) — a mostly-glue tax with one actionable generic, a never-built \
feature, and a churning uncovered file"
.to_string()
}
fn manifests(&self) -> BTreeMap<String, zombie::ManifestFacts> {
[
(
"korp".to_string(),
zombie::ManifestFacts {
declared: ["spark41".to_string()].into(),
built: Default::default(),
build_space_known: true,
},
),
(
// knut declares no build space, so its `grpc` feature must NOT be
// called never-built — the false-positive guard, visible in the demo.
"knut".to_string(),
zombie::ManifestFacts {
declared: ["grpc".to_string()].into(),
built: Default::default(),
build_space_known: false,
},
),
]
.into()
}
}
/// **The six lenses as nornir's `Mimir.*` RPCs return them** — each is the lens
/// report's `state_json` (the observable surface: ranked findings + an
/// `evidence_note` naming missing feeds by table), carried verbatim as a
/// [`serde_json::Value`]. The pane RENDERS these in the thin path and adds no numbers
/// of its own, so a thin reading is exactly what the server computed.
#[derive(Clone, Debug, Default)]
pub struct RemoteReports {
pub bloat: serde_json::Value,
pub zombie: serde_json::Value,
pub risk: serde_json::Value,
pub weak_tests: serde_json::Value,
pub bench_gaps: serde_json::Value,
pub similar_code: serde_json::Value,
/// `Mimir.Villa` — impure / shell-out heresy over the CPG (the new lens).
pub villa: serde_json::Value,
/// `Mimir.Churn` — the frequency filter (high/low/band-pass) over the change-series.
pub churn: serde_json::Value,
/// `Mimir.Anomaly` — statistical outliers on the change-series.
pub anomaly: serde_json::Value,
/// `Mimir.WorldImpact` — changes ranked by world + constellation downstream.
pub world_impact: serde_json::Value,
/// `Mimir.BinaryDrift` — source(AST) vs binary(DWARF) attestation: symbols/edges
/// in the compiled binary not derivable from the audited source.
pub binary_drift: serde_json::Value,
/// `Mimir.SemanticClones` — the Type-4 twin of SimilarCode: independent
/// RE-IMPLEMENTATIONS (high cosine, LOW structural agreement), the exact
/// complement of the pair space SimilarCode keeps. Lens 12 on the chain since
/// 2026-08-25; before that it was foldable only on demand, which is why this
/// pane had no view for it at all.
pub semantic_clones: serde_json::Value,
/// `Mimir.DataReach` — which code touches which warehouse table, who reaches it.
pub data_reach: serde_json::Value,
/// `Mimir.Trust` — bottleneck trust from the entry surface (act / hold / refuse).
pub trust: serde_json::Value,
/// `Mimir.NameCollision` — CALLS landing on merged ids (call-graph soundness).
pub name_collision: serde_json::Value,
/// `Mimir.SymbolIdentity` — own definitions whose id the fold merged.
pub symbol_identity: serde_json::Value,
/// `Mimir.UndocApi` — undocumented public API ranked by churn.
pub undoc_api: serde_json::Value,
/// `Mimir.ReachableVuln` — advisories gated on whether own code calls the crate.
pub reachable_vuln: serde_json::Value,
/// `Mimir.Subclone` — SUB-ITEM clone regions of ONE repo's source: nornir's
/// SOURCE-door lens (it folds source text with the room's item spans, not a
/// warehouse table — `nornir::mimir_lens::subclone` module docs). PER-REPO by
/// construction: `repo = ""` is the lens's own REFUSAL naming the askable repos
/// (`repos_available`); the pane renders that refusal and makes each named repo
/// a click — never a "clean constellation".
pub subclone: serde_json::Value,
/// `Mimir.SemanticTwins` — v1's semantic twins on v2: Type-4 twins (cosine ≥
/// 0.882 AND structural ≤ 0.35 AND a body written differently AND mutual kNN
/// k = 8) and Type-3 copies, each pair with both members and a `why`.
pub semantic_twins: serde_json::Value,
/// `Mimir.GroupTwins` — the same semantic GROUP of statements in two functions
/// of ONE repo (nornir's second SOURCE-door lens). PER-REPO like subclone:
/// `repo = ""` is its REFUSAL naming `repos_available`, rendered as a refusal
/// with each repo a click — never as "no twins".
pub group_twins: serde_json::Value,
/// `Mimir.ReimplementsShared` — a crate that imports crate B and holds a copy
/// or twin of a B function: one row per function to replace, both sides.
pub reimplements_shared: serde_json::Value,
}
/// **Every lens the thin pane asks**: nornir's lens name (what the server
/// dispatches and every answer's `kind` says) beside the key the pane's state
/// carries it under. 19 of `nornir::mimir_lens::LENSES` (the 18 plus
/// `semantic_twins`), plus the two SOURCE-door lenses `subclone` and `group_twins` —
/// the SOURCE-door lens nornir's `answer()` dispatches beside them (it is not in
/// `LENSES` because it has no table feed and, so far, no `Mimir.*` verb on
/// nornir's own proto; dwarves' trimmed proto declares the verb so the row works
/// the day it lands, and until then a live server's `Unimplemented` is shown as
/// this one lens's error).
pub const THIN_LENSES: [(&str, &str); 22] = [
("generic_cost", "bloat"),
("zombie_features", "zombie"),
("change_risk", "risk"),
("weak_tests", "weak_tests"),
("bench_gaps", "bench_gaps"),
("similar_code", "similar_code"),
("semantic_clones", "semantic_clones"),
("villa", "villa"),
("churn", "churn"),
("anomaly", "anomaly"),
("world_impact", "world_impact"),
("binary_drift", "binary_drift"),
("data_reach", "data_reach"),
("trust", "trust"),
("name_collision", "name_collision"),
("symbol_identity", "symbol_identity"),
("undoc_api", "undoc_api"),
("reachable_vuln", "reachable_vuln"),
("subclone", "subclone"),
// The twins rows (2026-10-20): v1's semantic twins on v2, and the group twins
// of ONE repo's source (per-repo, its refusal names the askable repos).
("semantic_twins", "semantic_twins"),
("group_twins", "group_twins"),
// A library re-implemented by a crate that imports it (2026-10-22).
("reimplements_shared", "reimplements_shared"),
];
/// One lens's answer, landing on its own. `last` is false while the server says
/// the lens is `queued` / `running` on jera: its worker asks again.
#[derive(Clone, Debug)]
pub struct LensUpdate {
pub lens: &'static str,
pub answer: serde_json::Value,
pub last: bool,
}
/// A lens that has no answer yet: asked, nothing back (`pending`).
pub fn pending_envelope(lens: &str) -> serde_json::Value {
serde_json::json!({
"kind": lens,
"worst": [],
"feeds": [],
"evidence_note": format!("{lens}: asked — waiting for the server's answer"),
"lens_meta": { "state": "pending", "lens": lens },
})
}
/// A lens whose call FAILED (transport, auth, `Unimplemented`, a refusal by status):
/// this lens's error, never the tab's. The message is the call's own.
pub fn error_envelope(lens: &str, msg: &str) -> serde_json::Value {
serde_json::json!({
"kind": lens,
"worst": [],
"feeds": [],
"evidence_note": format!("{lens}: the call failed — {msg}"),
"lens_meta": { "state": "error", "lens": lens, "error": msg },
})
}
impl RemoteReports {
/// The answer of `lens` (nornir's name).
pub fn slot(&self, lens: &str) -> Option<&serde_json::Value> {
Some(match lens {
"generic_cost" => &self.bloat,
"zombie_features" => &self.zombie,
"change_risk" => &self.risk,
"weak_tests" => &self.weak_tests,
"bench_gaps" => &self.bench_gaps,
"similar_code" => &self.similar_code,
"semantic_clones" => &self.semantic_clones,
"villa" => &self.villa,
"churn" => &self.churn,
"anomaly" => &self.anomaly,
"world_impact" => &self.world_impact,
"binary_drift" => &self.binary_drift,
"data_reach" => &self.data_reach,
"trust" => &self.trust,
"name_collision" => &self.name_collision,
"symbol_identity" => &self.symbol_identity,
"undoc_api" => &self.undoc_api,
"reachable_vuln" => &self.reachable_vuln,
"subclone" => &self.subclone,
"semantic_twins" => &self.semantic_twins,
"group_twins" => &self.group_twins,
"reimplements_shared" => &self.reimplements_shared,
_ => return None,
})
}
pub fn slot_mut(&mut self, lens: &str) -> Option<&mut serde_json::Value> {
Some(match lens {
"generic_cost" => &mut self.bloat,
"zombie_features" => &mut self.zombie,
"change_risk" => &mut self.risk,
"weak_tests" => &mut self.weak_tests,
"bench_gaps" => &mut self.bench_gaps,
"similar_code" => &mut self.similar_code,
"semantic_clones" => &mut self.semantic_clones,
"villa" => &mut self.villa,
"churn" => &mut self.churn,
"anomaly" => &mut self.anomaly,
"world_impact" => &mut self.world_impact,
"binary_drift" => &mut self.binary_drift,
"data_reach" => &mut self.data_reach,
"trust" => &mut self.trust,
"name_collision" => &mut self.name_collision,
"symbol_identity" => &mut self.symbol_identity,
"undoc_api" => &mut self.undoc_api,
"reachable_vuln" => &mut self.reachable_vuln,
"subclone" => &mut self.subclone,
"semantic_twins" => &mut self.semantic_twins,
"group_twins" => &mut self.group_twins,
"reimplements_shared" => &mut self.reimplements_shared,
_ => return None,
})
}
/// Every lens asked, none answered yet.
pub fn all_pending() -> Self {
let mut r = Self::default();
for (lens, _) in THIN_LENSES {
if let Some(v) = r.slot_mut(lens) {
*v = pending_envelope(lens);
}
}
r
}
/// The lenses whose answer is still to come (asked, queued or running).
pub fn waiting(&self) -> Vec<(&'static str, String)> {
THIN_LENSES
.iter()
.filter_map(|(lens, _)| {
let v = self.slot(lens)?;
let w = lens_state_word(v);
matches!(w, "pending" | "queued" | "running").then(|| (*lens, w.to_string()))
})
.collect()
}
/// `Some(reason)` when EVERY lens failed — a server nobody reached, not 21
/// separate findings: the pane says UNAVAILABLE once, with the first reason.
pub fn all_failed(&self) -> Option<String> {
let mut first = None;
for (lens, _) in THIN_LENSES {
let v = self.slot(lens)?;
if lens_state_word(v) != "error" {
return None;
}
first.get_or_insert_with(|| {
v.pointer("/lens_meta/error")
.and_then(|e| e.as_str())
.unwrap_or("")
.to_string()
});
}
first
}
}
/// How long a lens the server keeps `queued` / `running` is asked again before
/// the tab stops asking (the job itself runs on).
const RE_ASK_BOUND: std::time::Duration = std::time::Duration::from_secs(20 * 60);
/// **The THIN lens source: nornir's six `Mimir.*` RPCs over gRPC.**
///
/// The shipped source when dwarves runs THIN against a nornir-server (t14s against
/// oden): the server HAS the warehouse and the fresh data, so a thin pane asks IT to
/// fold the lenses rather than trying to fold locally over a warehouse it does not
/// own. The QUERY half of every lens lives in nornir (ARCHITECTURE L5a); this is the
/// client for the `Mimir.{GenericCost,ZombieFeatures,ChangeRisk,SimilarCode,
/// WeakTests,BenchGaps}` reads. The fold code is IDENTICAL on both sides (it was
/// lifted verbatim into nornir), so the thin finding-set equals the local fold for
/// the same warehouse.
#[derive(Clone)]
pub struct RemoteLensSource {
pub endpoint: String,
pub token: String,
pub workspace: String,
/// Optional repo filter (""=all repos), forwarded to every lens RPC.
pub repo: String,
/// The `Mimir.Subclone` repo scope — its OWN knob, not [`repo`](Self::repo):
/// subclone is per-repo by construction, and `""` deliberately asks for the
/// lens's refusal (which names the askable repos) while every other lens
/// reads `""` as "all repos".
pub subclone_repo: String,
/// The `Mimir.GroupTwins` repo scope — its own knob, as subclone's.
pub group_twins_repo: String,
/// Result cap per lens (0 = each lens's server default).
pub limit: u32,
/// GenericCost instantiation floor (0 = the server default of 2).
pub min_instantiations: u32,
/// `Mimir.Churn` band: "high" / "low" / "band" (default "high").
pub churn_band: String,
/// `Mimir.Churn` low cutoff (band-/low-pass change-frequency threshold).
pub churn_cutoff_low: i64,
/// `Mimir.Churn` high cutoff (band-/high-pass change-frequency threshold).
pub churn_cutoff_high: i64,
}
impl RemoteLensSource {
pub fn new(
endpoint: impl Into<String>,
token: impl Into<String>,
workspace: impl Into<String>,
) -> Self {
Self {
endpoint: endpoint.into(),
token: token.into(),
workspace: workspace.into(),
repo: String::new(),
subclone_repo: String::new(),
group_twins_repo: String::new(),
limit: 0,
min_instantiations: 0,
churn_band: "high".to_string(),
churn_cutoff_low: 2,
churn_cutoff_high: 5,
}
}
}
impl LensSource for RemoteLensSource {
/// A remote source folds on the SERVER — it has no local `Sources` to hand back.
/// The pane never calls this (it takes the [`remote`](LensSource::remote) path); it
/// returns an honest error rather than a silent empty in case something does.
fn load(&self) -> Result<Sources, String> {
// CODED `dwarves-mimir-3`: a remote (server-folding) source has no local warehouse.
Err(dwarves::dwarves_err!(
mimir,
3,
"RemoteLensSource folds on the server via Mimir.* RPCs — it has no local \
warehouse to load"
)
.to_string())
}
fn describe(&self) -> String {
format!(
"nornir Mimir.* RPCs @ {} / {}",
self.endpoint, self.workspace
)
}
fn folds_on_the_server(&self) -> bool {
true
}
fn remote(&self) -> Option<Result<RemoteReports, String>> {
// Every lens on its own: one that fails or waits does not take the others
// with it. Re-asks a `queued` / `running` lens until it lands.
let mut r = RemoteReports::all_pending();
for (lens, _) in THIN_LENSES {
self.ask_until_answered(lens, |u| {
if let Some(slot) = r.slot_mut(u.lens) {
*slot = u.answer;
}
true
});
}
Some(match r.all_failed() {
Some(why) => Err(why),
None => Ok(r),
})
}
fn set_churn_params(&mut self, band: &str, cutoff_low: i64, cutoff_high: i64) {
self.churn_band = band.to_string();
self.churn_cutoff_low = cutoff_low;
self.churn_cutoff_high = cutoff_high;
}
fn set_subclone_repo(&mut self, repo: &str) {
self.subclone_repo = repo.to_string();
}
fn set_group_twins_repo(&mut self, repo: &str) {
self.group_twins_repo = repo.to_string();
}
/// One lens again, on its own worker — a CLONE of this source, exactly as
/// [`remote_spawn`](LensSource::remote_spawn) spawns each of its workers.
fn remote_spawn_one(
&self,
lens: &'static str,
) -> Option<std::sync::mpsc::Receiver<LensUpdate>> {
let (tx, rx) = std::sync::mpsc::channel();
let me = self.clone();
std::thread::Builder::new()
.name(format!("mimir-lens-{lens}-again"))
.spawn(move || me.ask_until_answered(lens, |u| tx.send(u).is_ok()))
.ok()?;
Some(rx)
}
/// One worker per lens over a CLONE of this source (strings and knobs —
/// nothing shared): each sends its lens's answers as they land, re-asking a
/// queued one, and the channel closes when the last worker is done.
fn remote_spawn(&self) -> Option<std::sync::mpsc::Receiver<LensUpdate>> {
let (tx, rx) = std::sync::mpsc::channel();
for (lens, _) in THIN_LENSES {
let me = self.clone();
let tx = tx.clone();
std::thread::Builder::new()
.name(format!("mimir-lens-{lens}"))
.spawn(move || me.ask_until_answered(lens, |u| tx.send(u).is_ok()))
.ok()?;
}
Some(rx)
}
}
impl RemoteLensSource {
/// Ask `lens` once. A failed call is THIS lens's error envelope.
pub fn ask_once(&self, lens: &str) -> serde_json::Value {
use nornir_warehouse_embed::mimir as m;
let (e, t, w, repo, lim) = (
self.endpoint.as_str(),
self.token.as_str(),
self.workspace.as_str(),
self.repo.as_str(),
self.limit,
);
let r = match lens {
"generic_cost" => m::generic_cost(e, t, w, repo, self.min_instantiations, lim),
"zombie_features" => m::zombie_features(e, t, w, repo, lim),
"change_risk" => m::change_risk(e, t, w, repo, lim),
"weak_tests" => m::weak_tests(e, t, w, repo, lim),
"bench_gaps" => m::bench_gaps(e, t, w, repo, lim),
"similar_code" => m::similar_code(e, t, w, repo, lim),
"semantic_clones" => m::semantic_clones(e, t, w, repo, lim),
"villa" => m::villa(e, t, w, repo, lim),
"churn" => m::churn(
e,
t,
w,
repo,
&self.churn_band,
self.churn_cutoff_low,
self.churn_cutoff_high,
lim,
),
"anomaly" => m::anomaly(e, t, w, repo, 0.0, lim),
"world_impact" => m::world_impact(e, t, w, repo, lim),
"binary_drift" => m::binary_drift(e, t, w, repo, lim),
"data_reach" => m::data_reach(e, t, w, repo, lim),
"trust" => m::trust(e, t, w, repo, 0.0, 0.0, lim),
"name_collision" => m::name_collision(e, t, w, repo, lim),
"symbol_identity" => m::symbol_identity(e, t, w, repo, lim),
"undoc_api" => m::undoc_api(e, t, w, repo, 0, lim),
"reachable_vuln" => m::reachable_vuln(e, t, w, repo, lim),
// The SOURCE-door lens rides its OWN repo knob: per-repo by
// construction, and "" asks for the refusal that names the repos.
"subclone" => m::subclone(e, t, w, &self.subclone_repo, lim),
"semantic_twins" => m::semantic_twins(e, t, w, repo, lim),
// The second SOURCE-door lens, on its own repo knob like subclone.
"group_twins" => m::group_twins(e, t, w, &self.group_twins_repo, lim),
"reimplements_shared" => m::reimplements_shared(e, t, w, repo, lim),
other => return error_envelope(other, "not a lens this pane asks"),
};
r.unwrap_or_else(|x| error_envelope(lens, &x.to_string()))
}
/// Ask `lens` until its answer is not `queued` / `running`, handing every
/// answer to `send` (which returns false when nobody listens any more).
pub fn ask_until_answered(&self, lens: &'static str, send: impl FnMut(LensUpdate) -> bool) {
drive_until_answered(lens, || self.ask_once(lens), send, RE_ASK_BOUND);
}
}
/// **A failed connect is not the lens's answer.** Every ask opens its own channel
/// with a 3 s connect timeout; over tailscale, with 21 lenses polling a loaded
/// server, one connect in many misses it. Such an ask is asked again within the
/// bound, so one dropped connect no longer pins the lens at ERROR for good.
fn is_transport_failure(v: &serde_json::Value) -> bool {
lens_state_word(v) == "error"
&& v.pointer("/lens_meta/error")
.and_then(|e| e.as_str())
.is_some_and(|e| {
e.contains("transport error") || e.contains("Unavailable") || e.contains("connect")
})
}
/// The ask loop behind [`RemoteLensSource::ask_until_answered`]: `queued` / `running`
/// and transport failures are asked again (pause 300 ms growing to 3 s) until
/// `bound`; a transport failure past the bound is handed on as it came.
fn drive_until_answered(
lens: &'static str,
mut ask: impl FnMut() -> serde_json::Value,
mut send: impl FnMut(LensUpdate) -> bool,
bound: std::time::Duration,
) {
let t0 = std::time::Instant::now();
let mut pause = std::time::Duration::from_millis(300);
loop {
let answer = ask();
let word = lens_state_word(&answer).to_string();
let waiting = matches!(word.as_str(), "queued" | "running");
let retry = is_transport_failure(&answer) && t0.elapsed() <= bound;
if waiting && t0.elapsed() > bound {
let msg = format!(
"still {word} on the server after {}s — the tab stopped asking; the job runs on, ask again later",
t0.elapsed().as_secs()
);
send(LensUpdate { lens, answer: error_envelope(lens, &msg), last: true });
return;
}
if !retry && (!send(LensUpdate { lens, answer, last: !waiting }) || !waiting) {
return;
}
std::thread::sleep(pause);
pause = (pause * 3 / 2).min(std::time::Duration::from_secs(3));
}
}
/// Which lens the pane is showing.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum View {
Bloat,
Zombie,
Risk,
/// Tests that cannot go red (`Mimir.WeakTests`).
WeakTests,
/// Hot paths with no bencher (`Mimir.BenchGaps`).
BenchGaps,
/// The relocated dupes query (`Mimir.SimilarCode`).
SimilarCode,
/// Impure / shell-out heresy (`Mimir.Villa`) — the new purity lens.
Villa,
/// Drift between successive Build Thing artifacts of one variant
/// (`Mimir.BinaryDrift`): what changed in the bytes, and whether the
/// provenance (commit, toolchain) explains it.
BinaryDrift,
/// Cross-repo qualified-path collisions (`Mimir.NameCollision`) — call-graph
/// soundness, folded LOCALLY over `symbol_facts` (the fat/embedded leg).
NameCollision,
/// The Brokkr software-criteria scorecard — the software-as-a-graph quality
/// view: one factor per lens, folded into a grade.
Quality,
/// `Mimir.Churn` — the frequency filter (high/low/band-pass) over the change-series.
Churn,
/// `Mimir.Anomaly` — statistical outliers on the change-series.
Anomaly,
/// `Mimir.WorldImpact` — changes ranked by world + constellation downstream.
WorldImpact,
/// Independent re-implementations (`Mimir.SemanticClones`) — SimilarCode's
/// AND-gate inverted. A pair belongs to one lens or the other, never both.
SemanticClones,
/// `Mimir.DataReach` — which code reaches which warehouse table.
DataReach,
/// `Mimir.Trust` — bottleneck trust: act / hold / refuse.
Trust,
/// `Mimir.SymbolIdentity` — own definitions whose id the fold merged.
SymbolIdentity,
/// `Mimir.UndocApi` — undocumented public API ranked by churn.
UndocApi,
/// `Mimir.ReachableVuln` — advisories gated on reachability.
ReachableVuln,
/// `Mimir.Subclone` — SUB-ITEM clone regions of ONE repo's source (nornir's
/// SOURCE-door lens). With no repo chosen it renders the lens's REFUSAL and
/// each named repo is a click; with one, one row per finding, clickable
/// through to its source.
Subclone,
/// `Mimir.SemanticTwins` — Type-4 twins + Type-3 copies, one row per pair with
/// both members; a pair click opens both members' source side by side.
SemanticTwins,
/// `Mimir.GroupTwins` — the same statement group in two functions of ONE repo
/// (per-repo: its refusal names the repos, each a click).
GroupTwins,
/// `Mimir.ReimplementsShared` — a function to replace with a call to the
/// library its crate already imports, one row per pair (both sides).
ReimplementsShared,
/// The L5a staging note + the specified nornir RPCs.
Belongs,
}
impl View {
fn as_str(self) -> &'static str {
match self {
View::Bloat => "bloat",
View::Zombie => "zombie",
View::Risk => "risk",
View::WeakTests => "weak_tests",
View::BenchGaps => "bench_gaps",
View::SimilarCode => "similar_code",
View::SemanticClones => "semantic_clones",
View::Villa => "villa",
View::BinaryDrift => "binary_drift",
View::NameCollision => "name_collision",
View::Quality => "quality",
View::Churn => "churn",
View::Anomaly => "anomaly",
View::WorldImpact => "world_impact",
View::DataReach => "data_reach",
View::Trust => "trust",
View::SymbolIdentity => "symbol_identity",
View::UndocApi => "undoc_api",
View::ReachableVuln => "reachable_vuln",
View::Subclone => "subclone",
View::SemanticTwins => "semantic_twins",
View::GroupTwins => "group_twins",
View::ReimplementsShared => "reimplements_shared",
View::Belongs => "belongs",
}
}
fn parse(s: &str) -> Option<Self> {
match s {
"bloat" => Some(View::Bloat),
"zombie" => Some(View::Zombie),
"risk" => Some(View::Risk),
"weak_tests" => Some(View::WeakTests),
"bench_gaps" => Some(View::BenchGaps),
"similar_code" => Some(View::SimilarCode),
"semantic_clones" => Some(View::SemanticClones),
"villa" => Some(View::Villa),
"binary_drift" => Some(View::BinaryDrift),
"name_collision" => Some(View::NameCollision),
"quality" => Some(View::Quality),
"churn" => Some(View::Churn),
"anomaly" => Some(View::Anomaly),
"world_impact" => Some(View::WorldImpact),
"data_reach" => Some(View::DataReach),
"trust" => Some(View::Trust),
"symbol_identity" => Some(View::SymbolIdentity),
"undoc_api" => Some(View::UndocApi),
"reachable_vuln" => Some(View::ReachableVuln),
"subclone" => Some(View::Subclone),
"semantic_twins" => Some(View::SemanticTwins),
"group_twins" => Some(View::GroupTwins),
"reimplements_shared" => Some(View::ReimplementsShared),
"belongs" => Some(View::Belongs),
_ => None,
}
}
}
/// **The Mímir menu — the ONE grouped set of code-graph / quality operations.**
///
/// The owner: *"alla operationer för att hitta dubbletter och liknande skall ligga
/// under Mimir-meny, nu ligger de utspridda."* This const is the single source for
/// the pane's in-pane selector AND the top-level **Mímir** menubar group (dwarves-gui
/// `tabbed.rs`), so the consolidation cannot drift between the two surfaces. Each
/// entry is `(view_id, label)` where `view_id` matches [`View::as_str`] — the menubar
/// fires `mimir:<view_id>` to jump to the Lenses tab and select that lens.
pub const MIMIR_MENU: &[(&str, &str)] = &[
("quality", "⚖ Quality (Brokkr)"),
("similar_code", "🔁 Similar Code"),
("semantic_twins", "👯 Semantic twins"),
("reimplements_shared", "♻ Re-implemented libraries (call the library)"),
("zombie", "🧟 Zombie Features"),
("risk", "🎯 Change Risk"),
("weak_tests", "🧪 Weak Tests"),
("bench_gaps", "⏱ Bench Gaps"),
("bloat", "📦 Generic Cost"),
("villa", "⚠ Villa (heresy)"),
// The change/anomaly operations over the temporal graph — first-class entries under
// the ONE Mímir menu, labelled by the OPERATION (no "lens" word — Mímir explains it).
("churn", "📈 Churn (high/low/band-pass)"),
("anomaly", "⚡ Anomalies"),
("world_impact", "🌍 World impact"),
("binary_drift", "🧬 Binary drift (build ↔ build)"),
// Cross-repo qualified-path collisions — call-graph soundness, folded locally.
("name_collision", "🧭 Name collisions (graph soundness)"),
("symbol_identity", "🪪 Symbol identity (merged ids)"),
("data_reach", "🗄 Data reach (who touches which table)"),
("trust", "🛡 Trust (act / hold / refuse)"),
("undoc_api", "📝 Undocumented public API"),
("reachable_vuln", "🐞 Reachable vulnerabilities"),
// The SOURCE-door dedup operation: shared runs SMALLER than the item they sit
// in, which no chunk-keyed tier can state — one repo at a time by construction.
("subclone", "🧩 Sub-item clones (one repo)"),
("group_twins", "🪢 Group twins (one repo)"),
("belongs", "① Where this belongs"),
];
/// One Brokkr software-criteria factor, scored from a lens's finding count.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrokkrFactor {
/// The lens this factor reads (`villa`, `similar_code`, …).
pub lens: &'static str,
/// The human name of the factor.
pub name: &'static str,
/// The Brokkr software-criterion this factor upholds.
pub criterion: &'static str,
/// The finding count from the lens — `None` when the lens did not compute
/// (`lens_meta.state` is not complete / partial / empty_fact): there is no count.
pub count: Option<u64>,
/// `pass` (0) · `warn` (1..=amber) · `fail` (> amber) · [`NOT_MEASURED`].
pub status: &'static str,
/// The lens's own `lens_meta.state` word, verbatim (`""` when it sent none).
pub state: String,
}
/// The status of a factor whose lens did not compute. Never `pass`.
pub const NOT_MEASURED: &str = "not measured";
fn grade(count: u64, amber: u64) -> &'static str {
if count == 0 {
"pass"
} else if count <= amber {
"warn"
} else {
"fail"
}
}
/// Count a lens's ranked findings — the array under `key` (falling back to a scalar
/// count field). The software-as-a-graph view is, at minimum, counts-per-lens.
fn lens_count(v: &serde_json::Value, key: &str, scalar: &str) -> u64 {
if let Some(n) = v.get(scalar).and_then(|x| x.as_u64()) {
return n;
}
v.get(key)
.and_then(|a| a.as_array())
.map(|a| a.len() as u64)
.unwrap_or(0)
}
/// **The Brokkr scorecard — software AS A GRAPH, graded by its lenses.**
///
/// Each lens is one factor of Brokkr's software criteria (`brokkr-software-criteria.md`):
/// the new Villa lens scores PURITY (baremetal — no shelling out to OS tooling), the
/// others score duplication, dead code, test strength and bench coverage. A pure fold
/// over the lens `state_json`s (it adds no numbers of its own — counts come from the
/// lenses), returned as the observable summary the Quality view renders.
pub fn brokkr_scorecard(r: &RemoteReports) -> serde_json::Value {
let heresies = lens_count(&r.villa, "findings", "heresies");
let bench_never = r
.bench_gaps
.get("per_repo")
.and_then(|a| a.as_array())
.map(|a| {
a.iter()
.filter(|rp| {
rp.get("never_benched")
.and_then(|b| b.as_bool())
.unwrap_or(false)
})
.count() as u64
})
.unwrap_or(0);
// A factor is graded only when its lens COMPUTED (`lens_meta.state` complete /
// partial / empty_fact). A lens that is not wired, refused, or empty because an
// input is missing has no count: grading its empty list would turn a missing
// producer into a "pass" — the fake zero this scorecard must never print.
let factor = |lens: &'static str,
report: &serde_json::Value,
name: &'static str,
criterion: &'static str,
count: u64,
amber: u64| {
let answer = LensAnswer::of(report);
BrokkrFactor {
lens,
name,
criterion,
count: answer.is_measured().then_some(count),
status: if answer.is_measured() {
grade(count, amber)
} else {
NOT_MEASURED
},
state: lens_state_word(report).to_string(),
}
};
let factors = [
factor(
"villa",
&r.villa,
"Purity (baremetal, no shell-out)",
"No dependence on OS distribution / package managers and their tooling — \
stay pure Rust, don't shell out.",
heresies,
0, // any heresy is a fail — this is the point of the lens
),
factor(
"similar_code",
&r.similar_code,
"Minimal duplication (REUSE)",
"Single or minimal dependencies — the fewer, the merrier; no copy-paste twins.",
lens_count(&r.similar_code, "top_clusters", "clusters"),
3,
),
factor(
"zombie",
&r.zombie,
"No dead features",
"Lean — no gated code no caller can reach.",
// `zombies` is the lens's TOTAL (its `worst` is only the page it sent).
lens_count(&r.zombie, "worst", "zombies"),
2,
),
factor(
"weak_tests",
&r.weak_tests,
"Tests can go red",
"Superb functionality is PROVEN — a test that cannot fail proves nothing.",
lens_count(&r.weak_tests, "worst", "weak"),
2,
),
factor(
"bench_gaps",
&r.bench_gaps,
"Hot paths benched",
"Superb functionality shown by zero-copy · multi-core · SIMD — measured, not asserted.",
bench_never,
1,
),
];
let fails = factors.iter().filter(|f| f.status == "fail").count();
let warns = factors.iter().filter(|f| f.status == "warn").count();
let unmeasured = factors.iter().filter(|f| f.status == NOT_MEASURED).count();
let overall = if fails > 0 {
"heresy" // at least one criterion is broken — true whatever the others say
} else if unmeasured > 0 {
"unknown" // nothing failed, but not every criterion was measured: not "clean"
} else if warns > 0 {
"warn"
} else {
"clean"
};
// A blunt 0..=100 score: full marks minus a graded penalty per factor. A SORT KEY
// for the scorecard, never a lone verdict — the factors are the columns. No score
// at all while a factor is unmeasured: full marks minus nothing is not a score.
let score: Option<i64> = (unmeasured == 0).then(|| {
(100 - factors
.iter()
.map(|f| match f.status {
"fail" => 20,
"warn" => 8,
_ => 0,
})
.sum::<i64>())
.max(0)
});
serde_json::json!({
"kind": "brokkr_quality",
"overall": overall,
"score": score,
"unmeasured": unmeasured,
"heresies": LensAnswer::of(&r.villa).is_measured().then_some(heresies),
"factors": factors.iter().map(|f| serde_json::json!({
"lens": f.lens,
"name": f.name,
"criterion": f.criterion,
"count": f.count,
"status": f.status,
"state": f.state,
})).collect::<Vec<_>>(),
"attribution": "Brokkr's software criteria (dwarves/brokkr-software-criteria.md) — \
the law the forge builds by. Each factor is one Mímir lens over the CPG.",
})
}
/// The **🔍 Lenses** pane.
pub struct LensPane {
title: String,
source: Box<dyn LensSource>,
bloat: Option<bloat::BloatReport>,
zombie: Option<zombie::ZombieReport>,
risk: Option<risk::RiskReport>,
/// The coverage index — carries the weak-tests report and the per-repo
/// bench-coverage verdicts (the two widened views), computed in the SAME
/// `scan()` so all six lenses read one warehouse read.
idx: Option<CoverageIndex>,
/// The name-collision reading — cross-repo qualified-path clashes over `symbol_facts`,
/// folded LOCALLY in the SAME `scan()` (it reads a table the pane already loads).
collide: Option<collide::CollisionReport>,
error: Option<String>,
view: View,
/// Show only the ACTIONABLE (named) generic groups in the bloat view.
actionable_only: bool,
/// The THIN reading: the six lenses as nornir's `Mimir.*` RPCs returned them.
/// `Some` ⇒ the source folded on the server and the pane renders its findings;
/// `None` ⇒ the fat/local path (the typed reports above).
remote: Option<RemoteReports>,
/// A server fold in flight (the worker's receiver + when it started). The frame
/// polls it; `wait()` blocks on it for the robot seam.
pending: Option<(std::sync::mpsc::Receiver<LensUpdate>, std::time::Instant)>,
/// The Churn frequency-filter controls (Change view): band + low/high cutoffs.
/// Threaded into the source before a thin scan (`Mimir.Churn`).
churn_band: String,
churn_cutoff_low: i64,
churn_cutoff_high: i64,
/// **Show the find→fix block** — the actionable next step per finding. On by default
/// (the fix IS the point of a lens); a read-only suggestion, never an applied edit.
show_fixes: bool,
/// Whether the read-on-open has already fired. Reset by
/// [`forget`](Self::forget) so a workspace change reads the new workspace.
auto_read: bool,
/// The **Similar Code sub-view** — the former top-level Dupes tab, folded in here
/// under Lenses (LAW 5 / the owner 2026-08-22: "IGEN dublett, skall inte ligga i top
/// menyn"). It READS `lens_similar_code` and carries the local-fold APPLY seam the
/// generic lens renderer has no place for. `None` on the fixture/robot constructors.
dupes: Option<Box<crate::dupes::DupesPane>>,
/// **The clone-source door** — the source reader a semantic-clone member pick
/// fills: `Knowledge.Source` on the member's moniker (the owner, 2026-10-18:
/// "make sure if click we see source code or at least its url"). Room-less on
/// the bare constructors (a pick then says so); `lens_pane` wires the forge.
code: crate::graph_source::GraphSourcePane,
/// **The subclone repo scope** — which ONE repo `Mimir.Subclone` folds (the
/// lens is per-repo by construction). `""` = none chosen: the lens answers its
/// refusal, which NAMES the askable repos, and each name rendered is a click
/// that writes this filter and re-asks the one lens.
subclone_repo: String,
/// One-lens RE-ASKS in flight (a subclone / group_twins repo click): their own
/// receivers, because the full scan's may still be draining other lenses when
/// the click lands.
reask_pending: Vec<std::sync::mpsc::Receiver<LensUpdate>>,
/// **The group_twins repo scope** — as [`subclone_repo`](Self::subclone_repo),
/// for the second SOURCE-door lens.
group_twins_repo: String,
/// **The twin pair's source, side by side** — the owner, 2026-10-20: every
/// twin member is a pointer to source. A pair click asks the room's
/// `Knowledge.Source` for EACH member (the server reads the text; the UI reads
/// no file) and the two answers are drawn beside each other.
twin_a: crate::graph_source::GraphSourcePane,
twin_b: crate::graph_source::GraphSourcePane,
/// Which pair is open: (lens, row of [`twin_rows`]).
twin_pick: Option<(&'static str, usize)>,
}
impl LensPane {
pub fn live(
endpoint: impl Into<String>,
token: impl Into<String>,
workspace: impl Into<String>,
) -> Self {
Self::with_source(Box::new(WarehouseLens::new(endpoint, token, workspace)))
}
/// **The THIN pane** — folds on the server over nornir's six `Mimir.*` RPCs. The
/// shipped choice when dwarves runs thin against a nornir-server (t14s → oden): the
/// server owns the warehouse, so it does the fold and the pane renders the result.
pub fn thin(
endpoint: impl Into<String>,
token: impl Into<String>,
workspace: impl Into<String>,
) -> Self {
Self::with_source(Box::new(RemoteLensSource::new(endpoint, token, workspace)))
}
pub fn with_source(source: Box<dyn LensSource>) -> Self {
Self {
title: "Lenses".to_string(),
source,
bloat: None,
zombie: None,
risk: None,
idx: None,
collide: None,
error: None,
view: View::Bloat,
actionable_only: false,
remote: None,
pending: None,
churn_band: "high".to_string(),
churn_cutoff_low: 2,
churn_cutoff_high: 5,
show_fixes: true,
auto_read: false,
dupes: None,
code: crate::graph_source::GraphSourcePane::new(),
subclone_repo: String::new(),
reask_pending: Vec::new(),
group_twins_repo: String::new(),
twin_a: crate::graph_source::GraphSourcePane::new(),
twin_b: crate::graph_source::GraphSourcePane::new(),
twin_pick: None,
}
}
/// Give the clone-source door a room to ask `Knowledge.Source` of. The shipped
/// pane gets the forge ([`lens_pane`]); tests hand a canned room.
pub fn set_code_room(&mut self, room: std::sync::Arc<dyn crate::room::RoomSource>) {
self.code = crate::graph_source::GraphSourcePane::new().with_room(room.clone());
self.twin_a = crate::graph_source::GraphSourcePane::new().with_room(room.clone());
self.twin_b = crate::graph_source::GraphSourcePane::new().with_room(room);
}
/// **The click on a twin pair**: row `i` of [`twin_rows`] for `lens` — ask the
/// room for BOTH members' source (scoped to each member's repo; a group twin's
/// statement lines highlighted) and show them side by side.
pub fn pick_twin_pair(&mut self, lens: &str, i: usize) {
let Some(r) = &self.remote else { return };
let Some(lens) = TWIN_LENSES.iter().copied().find(|l| *l == lens) else { return };
let Some(v) = r.slot(lens) else { return };
let Some(row) = twin_rows(lens, v).into_iter().nth(i) else { return };
for (pane, side) in [(&mut self.twin_a, &row.a), (&mut self.twin_b, &row.b)] {
pane.select_scoped(side.node(), (!side.repo.is_empty()).then(|| side.repo.clone()), line_ranges(&side.group_lines));
}
self.twin_pick = Some((lens, i));
}
/// **Both members' source, side by side** — under the pair list of `view`'s
/// lens when a pair of THAT lens is open: each column is a source pane that
/// asked the room for its member, headed `repo/file:start-end @ commit`.
fn twin_view(&mut self, ui: &mut egui::Ui, view: View) {
let Some((lens, _)) = self.twin_pick else { return };
if lens != view.as_str() {
return;
}
ui.separator();
ui.label(egui::RichText::new("⇆ both members' source").strong().color(TEAL));
let (a, b) = (&mut self.twin_a, &mut self.twin_b);
ui.columns(2, |cols| {
cols[0].push_id("twin-source-a", |ui| a.view(ui));
cols[1].push_id("twin-source-b", |ui| b.view(ui));
});
}
/// **The click on a repo the group_twins refusal names** — as
/// [`pick_subclone_repo`](Self::pick_subclone_repo): write the filter, re-ask
/// the ONE lens.
pub fn pick_group_twins_repo(&mut self, repo: &str) {
self.group_twins_repo = repo.to_string();
self.source.set_group_twins_repo(repo);
if let Some(rx) = self.source.remote_spawn_one("group_twins") {
if let Some(s) = self.remote.as_mut().and_then(|r| r.slot_mut("group_twins")) {
*s = pending_envelope("group_twins");
}
// a pair of the old reading no longer names this answer's rows
if matches!(self.twin_pick, Some(("group_twins", _))) {
self.twin_pick = None;
}
self.reask_pending.push(rx);
}
}
/// **The click on a semantic clone member**: member `m` of ranked finding `c`
/// (the thin `findings` order). Asks the room for THAT member's source by its
/// moniker — never by name — off the UI thread; the graph-source pane lands it.
pub fn pick_clone_member(&mut self, c: usize, m: usize) {
let Some(r) = &self.remote else { return };
let member = &r.semantic_clones["findings"][c]["members"][m];
let Some(id) = member["id"].as_str() else { return };
let label = match (member["module"].as_str(), member["ident"].as_str()) {
(Some(md), Some(i)) => format!("{md}::{i}"),
(None, Some(i)) => i.to_string(),
_ => id.to_string(),
};
self.code.select(crate::graph_source::NodeRef {
label,
moniker: Some(id.to_string()),
kind: crate::graph_source::NodeKind::Symbol,
});
}
/// **The click on a repo the subclone refusal names**: write the pane's
/// subclone repo filter and re-ask the ONE lens with it — never a whole
/// re-scan (every other lens's answer is about the same warehouse still).
pub fn pick_subclone_repo(&mut self, repo: &str) {
self.subclone_repo = repo.to_string();
self.source.set_subclone_repo(repo);
if let Some(rx) = self.source.remote_spawn_one("subclone") {
// The slot says `pending` while the re-ask flies, so the row never
// shows the old refusal as if it were the new repo's answer.
if let Some(s) = self.remote.as_mut().and_then(|r| r.slot_mut("subclone")) {
*s = pending_envelope("subclone");
}
self.reask_pending.push(rx);
}
}
/// **The click on a subclone finding**: ranked finding `i` of the thin `worst`
/// list. The answer carries file+lines (`location`), never a moniker, and
/// `Knowledge.Source` resolves a NODE (`moniker`, or `name`: a path / path
/// suffix) — never a file — so the ask goes through the SAME
/// [`GraphSourcePane`](crate::graph_source::GraphSourcePane) door as a
/// semantic-clone member, by the enclosing item's path derived from the lead
/// file (`src/util/esc.rs` → `util::esc`, a suffix the room resolves). A lead
/// file with no derivable path (`src/lib.rs`, `src/main.rs`) asks by the file
/// name and the room's answer says, honestly, that it names no node.
pub fn pick_subclone_finding(&mut self, i: usize) {
let Some(r) = &self.remote else { return };
let f = &r.subclone["worst"][i];
let lead = f["lead_file"].as_str().unwrap_or("");
if lead.is_empty() {
return;
}
let label = enclosing_path_of(lead).unwrap_or_else(|| lead.to_string());
self.code.select(crate::graph_source::NodeRef {
label,
moniker: None,
kind: crate::graph_source::NodeKind::Symbol,
});
}
/// **Drop the current reading** so the pane reads again on its next frame —
/// what a workspace change calls. Deliberately NOT a re-fold: the chain has
/// already folded the new workspace's lenses, or is about to.
pub fn forget(&mut self) {
self.remote = None;
self.bloat = None;
self.zombie = None;
self.risk = None;
self.idx = None;
self.collide = None;
self.error = None;
self.auto_read = false;
}
#[cfg(any(test, feature = "fixtures"))]
pub fn fixture() -> Self {
Self::with_source(Box::new(FixtureLens))
}
/// Run all three lenses. A failure keeps the previous reading and records why.
pub fn scan(&mut self) {
// Thread the Change view's Churn frequency-filter params into the source (a
// remote source forwards them to Mimir.Churn; a local source ignores them).
let (band, clo, chi) = (
self.churn_band.clone(),
self.churn_cutoff_low,
self.churn_cutoff_high,
);
self.source.set_churn_params(&band, clo, chi);
// Thread the subclone repo scope the same way (a remote source forwards it
// to Mimir.Subclone; a local source ignores it — the lens is thin-only).
let subclone_repo = self.subclone_repo.clone();
self.source.set_subclone_repo(&subclone_repo);
let group_twins_repo = self.group_twins_repo.clone();
self.source.set_group_twins_repo(&group_twins_repo);
// THIN, off-thread: the source folds on the server (Mimir.* RPCs) on a worker;
// the frame keeps painting and `poll()` lands the result. A scan pressed while
// one is in flight is not a second round trip.
if self.pending.is_some() {
return;
}
if let Some(rx) = self.source.remote_spawn() {
self.pending = Some((rx, std::time::Instant::now()));
return;
}
// THIN, synchronous fallback: a remote source without a spawn — render its
// returned findings, and DO NOT fold locally over a warehouse this host does
// not own.
if let Some(res) = self.source.remote() {
self.apply_remote(res);
return;
}
let sources = match self.source.load() {
Ok(s) => s,
Err(e) => {
self.error = Some(e);
return;
}
};
self.bloat = Some(bloat::analyze(&sources, 2, 25));
self.zombie = Some(zombie::analyze(&sources, &self.source.manifests(), 25));
// The risk lens's coverage input is the SAME test-reachability the coverage
// index reports — computed once, never a second opinion.
let mut cov_tables = BTreeMap::new();
for t in dwarves::coverage::TABLES {
cov_tables.insert(t.to_string(), sources.get(t).clone());
}
let borrowed: dwarves::coverage::Tables =
cov_tables.iter().map(|(k, v)| (k.as_str(), v)).collect();
let idx = dwarves::coverage::build(
"mimir lens",
&borrowed,
// the local fold reads warehouse tables; the code graph is nornir's to answer
&dwarves::coverage::GraphFeed::not_asked(LOCAL_FOLD_HAS_NO_GRAPH),
&[],
&Default::default(),
0,
1,
1,
);
self.risk = Some(risk::analyze(&sources, &idx.covered_by_repo, 25));
// The weak-tests report and the per-repo bench-coverage verdicts ride on the
// SAME index — the widened views add no second warehouse read.
self.idx = Some(idx);
// The name-collision lens folds the SAME `symbol_facts` this scan already loaded
// (graph soundness) — one more local fold, no extra warehouse read.
self.collide = Some(collide::analyze(&sources, 25));
self.error = None;
self.remote = None;
}
/// Land a server fold: the thin reading replaces the typed (fat) reports.
fn apply_remote(&mut self, res: Result<RemoteReports, String>) {
match res {
Ok(r) => {
self.remote = Some(r);
// The typed (fat) reports are not this reading's source of truth.
self.bloat = None;
self.zombie = None;
self.risk = None;
self.idx = None;
self.collide = None;
self.error = None;
}
Err(e) => self.error = Some(e),
}
}
/// Land ONE lens's answer: the reading exists from the first answer on, and
/// every other lens keeps what it had (its answer, or `pending`).
fn land(&mut self, u: LensUpdate) {
if self.remote.is_none() {
self.remote = Some(RemoteReports::all_pending());
// The typed (fat) reports are not this reading's source of truth.
self.bloat = None;
self.zombie = None;
self.risk = None;
self.idx = None;
self.collide = None;
}
self.error = None;
if let Some(slot) = self.remote.as_mut().and_then(|r| r.slot_mut(u.lens)) {
*slot = u.answer;
}
}
/// Every lens worker is done: a reading in which EVERY lens failed is a
/// server nobody reached — UNAVAILABLE once, with the reason.
fn settle(&mut self) {
match self.remote.as_ref().map(RemoteReports::all_failed) {
Some(Some(why)) => {
self.remote = None;
self.error = Some(why);
}
Some(None) => {}
None => {
self.error = Some("the lens workers ended before any lens answered".into())
}
}
}
/// Non-blocking: land every lens answer that has arrived. Called once per
/// frame by `ui()`.
pub fn poll(&mut self) {
loop {
let Some((rx, _)) = &self.pending else { break };
match rx.try_recv() {
Ok(u) => self.land(u),
Err(std::sync::mpsc::TryRecvError::Empty) => break,
Err(std::sync::mpsc::TryRecvError::Disconnected) => {
self.pending = None;
self.settle();
break;
}
}
}
// A one-lens RE-ASK (a repo click) lands on its own receiver: one lens,
// so its end is no reason to settle the whole reading.
let mut landed = Vec::new();
self.reask_pending.retain(|rx| loop {
match rx.try_recv() {
Ok(u) => landed.push(u),
Err(std::sync::mpsc::TryRecvError::Empty) => break true,
Err(std::sync::mpsc::TryRecvError::Disconnected) => break false,
}
});
for u in landed {
self.land(u);
}
}
/// Blocking: wait until every lens has its last answer. The ROBOT seam
/// (`update_json`) uses this so a scripted `scan` is complete when the call
/// returns; the window never does.
pub fn wait(&mut self) {
if let Some((rx, _)) = self.pending.take() {
for u in rx.iter() {
self.land(u);
}
self.settle();
}
for rx in std::mem::take(&mut self.reask_pending) {
for u in rx.iter() {
self.land(u);
}
}
}
/// Is a server fold in flight? (The full scan, or a subclone re-ask.)
pub fn is_pending(&self) -> bool {
self.pending.is_some() || !self.reask_pending.is_empty()
}
pub fn status(&self) -> String {
if let Some((_, since)) = &self.pending {
let waiting = self
.remote
.as_ref()
.map(RemoteReports::waiting)
.unwrap_or_else(|| THIN_LENSES.iter().map(|(l, _)| (*l, "pending".to_string())).collect());
let head = format!(
"{} of {} lenses answered · {:.0}s — waiting on: {} — each lens lands on its own; \
the window is not blocked",
THIN_LENSES.len() - waiting.len(),
THIN_LENSES.len(),
since.elapsed().as_secs_f64(),
waiting
.iter()
.map(|(l, w)| format!("{l} ({w})"))
.collect::<Vec<_>>()
.join(", ")
);
return match &self.remote {
Some(r) => format!("{head}\n{}", answer_status(r)),
None => head,
};
}
if let Some(e) = &self.error {
return format!("UNAVAILABLE — {e}");
}
if let Some(r) = &self.remote {
// The ANSWERS header first — what each lens rests on is the question an
// operator has before "how many findings", because a list from a lens
// that did not compute is worse than no list.
return format!("{}\n{}", answer_status(r), thin_status(r));
}
match (&self.bloat, &self.zombie, &self.risk) {
(Some(b), Some(z), Some(r)) => format!(
"{} artifact(s) taxed · {} zombie feature(s) ({} severe) · {} file(s) scored for risk",
b.artifacts.len(),
z.features.len(),
z.severe().len(),
r.files_scored
),
// There is no Scan button to press any more (768983f) — the pane reads
// itself on open and the folds are jera chain jobs. A status line that
// names a control the window does not carry sends the operator looking
// for it.
_ => "no reading yet — the pane reads itself on open".to_string(),
}
}
fn scanned(&self) -> bool {
self.bloat.is_some() || self.remote.is_some()
}
/// **The stable error CODE for a failed scan** ([`dwarves::errcode`]) — `None` when the
/// scan succeeded. A failed read is classified onto the code the underlying layer
/// already raised (the proxy's `dwarves-proxy-2` for a dead upstream travels up in the
/// error string), falling back to `dwarves-mimir-1` for a lens fold that did not answer
/// and `dwarves-warehouse-1` for a raw warehouse read that could not reach a server.
///
/// This is the field the robot suite asserts instead of matching `"UNAVAILABLE — …"`.
pub fn error_code(&self) -> Option<&'static str> {
let e = self.error.as_ref()?;
// A code raised further down (e.g. the proxy's dead-upstream dwarves-proxy-2)
// survives in the message — prefer it, it is the more precise diagnosis.
if let Some(found) = dwarves::errcode::parse(e) {
return Some(found.code);
}
// Otherwise classify by which leg failed: the thin (server-folded) lens path is
// mimir-1; the fat/local warehouse read is warehouse-1.
Some(if self.source.folds_on_the_server() {
"dwarves-mimir-1"
} else {
"dwarves-warehouse-1"
})
}
/// The full dual-channel scan error — the code AND the human reason. `None` when OK.
pub fn error_object(&self) -> Option<dwarves::errcode::DwarvesError> {
let reason = self.error.as_ref()?;
let code = self.error_code()?;
let e = dwarves::errcode::lookup(code)?;
Some(dwarves::errcode::DwarvesError::new(
e.subsystem,
e.n,
e.code,
reason.clone(),
))
}
/// The per-lens `state_json` for one view — the SAME value `state_json` emits and the
/// pane renders, whether folded on the server (thin) or locally (fat). The find→fix
/// layer reads this so a fix is derived from EXACTLY the finding on screen.
fn view_json(&self, view: View) -> serde_json::Value {
use serde_json::Value::Null;
if let Some(r) = &self.remote {
return match view {
View::Bloat => r.bloat.clone(),
View::Zombie => r.zombie.clone(),
View::Risk => r.risk.clone(),
View::WeakTests => r.weak_tests.clone(),
View::BenchGaps => r.bench_gaps.clone(),
View::SimilarCode => r.similar_code.clone(),
View::SemanticClones => r.semantic_clones.clone(),
View::Villa => r.villa.clone(),
View::BinaryDrift => r.binary_drift.clone(),
View::Churn => r.churn.clone(),
View::Anomaly => r.anomaly.clone(),
View::WorldImpact => r.world_impact.clone(),
View::Quality => brokkr_scorecard(r),
View::NameCollision => r.name_collision.clone(),
View::DataReach => r.data_reach.clone(),
View::Trust => r.trust.clone(),
View::SymbolIdentity => r.symbol_identity.clone(),
View::UndocApi => r.undoc_api.clone(),
View::ReachableVuln => r.reachable_vuln.clone(),
View::Subclone => r.subclone.clone(),
View::SemanticTwins => r.semantic_twins.clone(),
View::GroupTwins => r.group_twins.clone(),
View::ReimplementsShared => r.reimplements_shared.clone(),
View::Belongs => Null,
};
}
match view {
View::Bloat => self
.bloat
.as_ref()
.map(bloat::BloatReport::state_json)
.unwrap_or(Null),
View::Zombie => self
.zombie
.as_ref()
.map(zombie::ZombieReport::state_json)
.unwrap_or(Null),
View::Risk => self
.risk
.as_ref()
.map(risk::RiskReport::state_json)
.unwrap_or(Null),
View::WeakTests => self
.idx
.as_ref()
.and_then(|i| serde_json::to_value(&i.weak).ok())
.unwrap_or(Null),
View::BenchGaps => self
.idx
.as_ref()
.map(|i| {
i.repos
.iter()
.filter_map(|r| serde_json::to_value(&r.bench).ok())
.collect::<Vec<_>>()
})
.map(serde_json::Value::from)
.unwrap_or(Null),
// The name-collision lens folds LOCALLY (fat leg) — it reads `symbol_facts`,
// which this host already loaded, so a local reading DOES carry it.
View::NameCollision => self
.collide
.as_ref()
.map(collide::CollisionReport::state_json)
.unwrap_or(Null),
// Villa / Churn / Anomaly / WorldImpact / SimilarCode / BinaryDrift / Quality
// fold on nornir (thin-only) — a local reading has no value for them.
_ => Null,
}
}
/// The find→fix list for the current view — the actionable next step per finding.
fn current_fixes(&self) -> Vec<Fix> {
fixes_for(self.view, &self.view_json(self.view))
}
/// **The Change view's frequency-filter controls** — the signal-processing band
/// selector (high/low/band-pass) + the low/high change-frequency cutoffs, over the
/// warehouse graph's temporal churn. Changing a control re-runs the thin scan so
/// `Mimir.Churn` re-folds with the new band.
fn change_controls(&mut self, ui: &mut egui::Ui) {
let mut apply = false;
ui.horizontal_wrapped(|ui| {
ui.label(
egui::RichText::new("Frequency filter:")
.strong()
.color(TEAL),
);
for (id, lbl) in [
("high", "high-pass · volatile hotspots"),
("low", "low-pass · stable core"),
("band", "band-pass"),
] {
if ui.selectable_label(self.churn_band == id, lbl).clicked() {
self.churn_band = id.to_string();
apply = true;
}
}
});
ui.horizontal(|ui| {
ui.label("low cutoff");
// NO "Apply filter" button (owner 2026-08-25: SCAN is not a user action).
// Changing a cutoff IS the new question — the pane re-asks it directly
// rather than making the operator confirm that they meant what they just
// typed. `.changed()` fires on the edit, not on every frame the widget is
// on screen, so an untouched control costs nothing.
if ui
.add(egui::DragValue::new(&mut self.churn_cutoff_low))
.changed()
{
apply = true;
}
ui.label("high cutoff");
if ui
.add(egui::DragValue::new(&mut self.churn_cutoff_high))
.changed()
{
apply = true;
}
ui.label(
egui::RichText::new("commits_90d thresholds; hotspot rank = churn × complexity")
.small(),
);
});
if apply {
self.scan();
}
}
}
/// Why a LOCAL lens fold carries no test-reachability: it folds warehouse tables, and
/// the code graph is not a table — nornir's `Mimir.*` lenses answer it from the room.
const LOCAL_FOLD_HAS_NO_GRAPH: &str =
"the local lens fold reads warehouse tables only: the code graph (what a test reaches) is asked of nornir's `Mimir.*` lenses, not folded here";
fn chip(ui: &mut egui::Ui, text: &str, color: egui::Color32) {
ui.label(
egui::RichText::new(format!(" {text} "))
.small()
.strong()
.color(egui::Color32::WHITE)
.background_color(color),
);
}
const RED: egui::Color32 = egui::Color32::from_rgb(0xc6, 0x28, 0x28);
const TEAL: egui::Color32 = egui::Color32::from_rgb(0x00, 0x69, 0x5c);
const AMBER: egui::Color32 = egui::Color32::from_rgb(0x8d, 0x6e, 0x00);
fn kb(b: u64) -> String {
if b >= 1_048_576 {
format!("{:.1} MB", b as f64 / 1_048_576.0)
} else {
format!("{:.1} KB", b as f64 / 1024.0)
}
}
/// The MISSING-FEED callout every LOCAL-fold view leads with (the fold's own note).
fn evidence(ui: &mut egui::Ui, note: &str) {
let bad = note.contains("MISSING FEEDS");
ui.horizontal_wrapped(|ui| {
if bad {
chip(ui, "MISSING FEED", RED);
}
ui.label(egui::RichText::new(note).small());
});
}
/// **What one `Mimir.*` answer says about itself** — `lens_meta.state`, as nornir's
/// Mímir v2 stamps every lens envelope. The pane reads THIS before it reads a single
/// finding: a ranked list means something only in the first three states.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LensAnswer {
/// `complete` — computed, every input present.
Complete,
/// `partial` — computed with an input missing: every figure is a LOWER bound.
Partial,
/// `empty_fact` — computed, every input present, nothing found. A fact.
EmptyFact,
/// `empty_gap` — empty BECAUSE an input is missing. Not a verdict.
EmptyGap,
/// `not_wired` — the lens waits for a producer (named in the note).
NotWired,
/// The lens refused by state: `not_consumed`, `lake_unavailable`,
/// `driver_not_available`, `no_room`, `not_linked`, `invalid_argument`, `internal`.
Refused,
/// No `lens_meta.state`, or a word this client does not know — the answer says
/// nothing about itself, so nothing in it is read as a verdict.
Unstated,
/// `pending` (asked, nothing back yet), `queued` / `running` (a jera job on the
/// server): the answer is still to come. Not a verdict, and holds no other lens.
Waiting,
/// `error` — this lens's call failed. Its own error; the others are unaffected.
Failed,
}
impl LensAnswer {
/// Read the state off a lens envelope.
pub fn of(v: &serde_json::Value) -> Self {
match lens_state_word(v) {
"complete" => Self::Complete,
"partial" => Self::Partial,
"empty_fact" => Self::EmptyFact,
"empty_gap" => Self::EmptyGap,
"not_wired" => Self::NotWired,
"not_consumed"
| "lake_unavailable"
| "driver_not_available"
| "no_room"
| "not_linked"
| "invalid_argument"
| "internal" => Self::Refused,
"pending" | "queued" | "running" => Self::Waiting,
"error" => Self::Failed,
_ => Self::Unstated,
}
}
/// Did the lens COMPUTE — is its ranked list (also an empty one) a measurement?
pub fn is_measured(self) -> bool {
matches!(self, Self::Complete | Self::Partial | Self::EmptyFact)
}
}
/// The raw `lens_meta.state` word (`""` when the envelope carries none).
pub fn lens_state_word(v: &serde_json::Value) -> &str {
v.get("lens_meta")
.and_then(|m| m.get("state"))
.and_then(|x| x.as_str())
.unwrap_or("")
}
/// The chip a thin view leads with for a state that is not `complete`, and the
/// sentence to print when the answer carries no evidence text of its own.
fn answer_chip(v: &serde_json::Value) -> Option<(String, egui::Color32, &'static str)> {
let word = lens_state_word(v);
match LensAnswer::of(v) {
LensAnswer::Complete => None,
LensAnswer::Partial => Some((
"PARTIAL — LOWER BOUND".into(),
AMBER,
"computed with an input missing: every figure below is a lower bound",
)),
LensAnswer::EmptyFact => Some((
"NOTHING FOUND".into(),
TEAL,
"computed with every input present: nothing found (a fact, not a gap)",
)),
LensAnswer::EmptyGap => Some((
"EMPTY — INPUT MISSING".into(),
RED,
"empty because an input is missing: this is NOT a verdict",
)),
LensAnswer::NotWired => Some((
"NOT WIRED".into(),
RED,
"this lens waits for a producer: a missing producer, not an absence of findings",
)),
LensAnswer::Refused => Some((
word.to_uppercase().replace('_', " "),
RED,
"the lens refused: nothing was computed",
)),
LensAnswer::Waiting => Some((
match word {
"queued" => "QUEUED ON JERA".into(),
"running" => "RUNNING ON JERA".into(),
_ => "ASKING".into(),
},
TEAL,
"still being computed on the server: it lands on its own and holds no other lens",
)),
LensAnswer::Failed => Some((
"ERROR".into(),
RED,
"this lens's call failed: the other lenses are not affected",
)),
LensAnswer::Unstated => Some((
if word.is_empty() {
"NO STATE".into()
} else {
format!("UNKNOWN STATE `{word}`")
},
RED,
"the server did not say what this answer rests on (no Mímir v2 `lens_meta.state`): \
nothing below is read as a verdict",
)),
}
}
/// The callout every THIN view leads with: the answer's state as a chip, then the
/// answer's OWN evidence text (`evidence_note`) verbatim — or, when it sent none, the
/// sentence of its state. An empty ranked list is never drawn without one of the two.
fn thin_evidence(ui: &mut egui::Ui, v: &serde_json::Value) {
let note = thin_note(v);
let chipped = answer_chip(v);
ui.horizontal_wrapped(|ui| {
if LensAnswer::of(v) == LensAnswer::Waiting {
ui.spinner();
}
if let Some((label, color, _)) = &chipped {
chip(ui, label, *color);
}
let text = match (&chipped, note.is_empty()) {
(Some((_, _, fallback)), true) => (*fallback).to_string(),
_ => note,
};
ui.label(egui::RichText::new(text).small());
});
}
/// A whole-number field off a lens Value (0 when absent) — the thin status counters.
fn ju64(v: &serde_json::Value, key: &str) -> u64 {
v.get(key).and_then(|x| x.as_u64()).unwrap_or(0)
}
/// The one-line status for a THIN reading, built from the RPC Values (no numbers of
/// the pane's own).
/// The lenses of a thin reading, by the name nornir dispatches them under.
fn remote_lenses(r: &RemoteReports) -> Vec<(&'static str, &serde_json::Value)> {
THIN_LENSES
.iter()
.filter_map(|(lens, _)| Some((*lens, r.slot(lens)?)))
.collect()
}
/// **The answers header** — how many of the lenses COMPUTED, and for every one that
/// did not, which state it is in, by name:
/// `lens 9/12 answered · 7 complete · 1 partial (lower bound) · 1 empty: nothing found ·
/// EMPTY, INPUT MISSING: generic_cost, binary_drift · NOT WIRED: similar_code ·
/// feeds missing: …`.
///
/// Read off the `lens_meta.state` every `Mimir.*` answer carries since Mímir v2 (a
/// lens is computed on the request; there is no chain, no snapshot and no cache to
/// report), and off the `feeds` each fold recorded. An answer WITHOUT a state — a
/// server that is not Mímir v2 — is named as such and counted as not answered: its
/// list is not a verdict this client can vouch for.
pub fn answer_status(r: &RemoteReports) -> String {
let lenses = remote_lenses(r);
let (mut complete, mut partial, mut empty_fact) = (0usize, 0usize, 0usize);
let mut empty_gap: Vec<&str> = Vec::new();
let mut not_wired: Vec<&str> = Vec::new();
let mut refused: Vec<String> = Vec::new();
let mut unstated: Vec<String> = Vec::new();
let mut waiting: Vec<String> = Vec::new();
let mut failed: Vec<&str> = Vec::new();
let mut missing: std::collections::BTreeSet<String> = Default::default();
let total = lenses.len();
for (name, v) in lenses {
match LensAnswer::of(v) {
LensAnswer::Complete => complete += 1,
LensAnswer::Partial => partial += 1,
LensAnswer::EmptyFact => empty_fact += 1,
LensAnswer::EmptyGap => empty_gap.push(name),
LensAnswer::NotWired => not_wired.push(name),
LensAnswer::Refused => refused.push(format!("{name} [{}]", lens_state_word(v))),
LensAnswer::Waiting => waiting.push(format!("{name} [{}]", lens_state_word(v))),
LensAnswer::Failed => failed.push(name),
LensAnswer::Unstated => unstated.push(match lens_state_word(v) {
"" => name.to_string(),
word => format!("{name} [`{word}`]"),
}),
}
// The MISSING FEEDS come from each lens's own feed record — a missing
// producer is a fact about the warehouse, whatever the lens made of it.
if let Some(feeds) = v.get("feeds").and_then(|f| f.as_array()) {
for f in feeds {
if f.get("missing").and_then(|m| m.as_bool()) == Some(true) {
if let Some(t) = f.get("table").and_then(|t| t.as_str()) {
missing.insert(t.to_string());
}
}
}
}
}
let mut parts: Vec<String> = vec![format!(
"lens {}/{} answered",
complete + partial + empty_fact,
total
)];
if complete > 0 {
parts.push(format!("{complete} complete"));
}
if partial > 0 {
parts.push(format!("{partial} partial (lower bound)"));
}
if empty_fact > 0 {
parts.push(format!("{empty_fact} empty: nothing found"));
}
if !empty_gap.is_empty() {
parts.push(format!("EMPTY, INPUT MISSING: {}", empty_gap.join(", ")));
}
if !not_wired.is_empty() {
parts.push(format!("NOT WIRED: {}", not_wired.join(", ")));
}
if !refused.is_empty() {
parts.push(format!("REFUSED: {}", refused.join(", ")));
}
if !waiting.is_empty() {
parts.push(format!("WAITING: {}", waiting.join(", ")));
}
if !failed.is_empty() {
parts.push(format!("ERROR: {}", failed.join(", ")));
}
if !unstated.is_empty() {
parts.push(format!(
"NO STATE (not a Mímir v2 answer): {}",
unstated.join(", ")
));
}
parts.push(if missing.is_empty() {
"feeds missing: none".to_string()
} else {
format!(
"feeds missing: {}",
missing.iter().cloned().collect::<Vec<_>>().join(", ")
)
});
parts.join(" · ")
}
/// The three headline counts — each only where its lens COMPUTED. A lens that did not
/// says its state instead of a number: `0 zombie feature(s)` from a lens that is not
/// wired would be a zero nobody measured.
fn thin_status(r: &RemoteReports) -> String {
let count = |v: &serde_json::Value, n: u64, what: &str| -> String {
if LensAnswer::of(v).is_measured() {
format!("{n} {what}")
} else {
let word = lens_state_word(v);
format!(
"{what}: not measured [{}]",
if word.is_empty() { "no state" } else { word }
)
}
};
// `zombies` is the TOTAL the lens found; `worst` is the page it sent.
let shown = thin_arr(&r.zombie, "worst").len() as u64;
let zombies = r
.zombie
.get("zombies")
.and_then(|z| z.as_u64())
.unwrap_or(shown);
let zombie_what = if zombies > shown {
format!("zombie feature(s) ({shown} shown)")
} else {
"zombie feature(s)".to_string()
};
format!(
"{} · {} · {} (thin — folded on nornir via Mimir.*)",
count(&r.bloat, ju64(&r.bloat, "artifacts"), "artifact(s) taxed"),
count(&r.zombie, zombies, &zombie_what),
count(
&r.risk,
ju64(&r.risk, "files_scored"),
"file(s) scored for risk"
),
)
}
// ── the THIN renderer ────────────────────────────────────────────────────────────
// One renderer over the JSON the `Mimir.*` RPCs returned. It upholds BOTH
// prohibitions off the wire shape: every view LEADS with the `evidence_note` (so an
// empty ranked list never draws like an unfed table), and risk prints the three
// inputs (callers/churn/coverage) beside the score — never a composite number alone.
fn thin_note(v: &serde_json::Value) -> String {
v.get("evidence_note")
.and_then(|n| n.as_str())
.unwrap_or("")
.to_string()
}
fn thin_arr<'a>(v: &'a serde_json::Value, key: &str) -> &'a [serde_json::Value] {
v.get(key)
.and_then(|a| a.as_array())
.map(|a| a.as_slice())
.unwrap_or(&[])
}
fn thin_s(v: &serde_json::Value, k: &str) -> String {
match v.get(k) {
Some(serde_json::Value::String(s)) => s.clone(),
Some(other) => other.to_string(),
None => String::new(),
}
}
fn thin_n(v: &serde_json::Value, k: &str) -> f64 {
v.get(k).and_then(|x| x.as_f64()).unwrap_or(0.0)
}
/// The enclosing item's module path a source file implies — the handle a subclone
/// finding (file+lines, no moniker) asks `Knowledge.Source` by. The lead file is
/// checkout-relative (`crates/x/src/a/b.rs`); everything after its LAST `src/`
/// maps `/`→`::` with `.rs` and a trailing `/mod` dropped, so `src/util/esc.rs` →
/// `util::esc` — a path SUFFIX the room resolves. `None` for a crate root
/// (`src/lib.rs`, `src/main.rs`): there is no sub-path to name.
fn enclosing_path_of(file: &str) -> Option<String> {
let norm = file.replace('\\', "/");
let rest: &str = match norm.rfind("/src/") {
Some(i) => &norm[i + 5..],
None => norm.strip_prefix("src/")?,
};
let rest = rest.strip_suffix(".rs")?;
let rest = rest.strip_suffix("/mod").unwrap_or(rest);
if rest.is_empty() || rest == "lib" || rest == "main" {
return None;
}
Some(rest.replace('/', "::"))
}
/// What a frame of the thin view was clicked on (applied after the render, which
/// only borrows the reading).
#[derive(Default)]
struct Clicks {
/// A semantic-clone member: (finding, member).
pick: Option<(usize, usize)>,
/// A repo the subclone refusal named.
sub_repo: Option<String>,
/// A subclone finding.
sub_find: Option<usize>,
/// A repo the group_twins refusal named.
gt_repo: Option<String>,
/// A twin pair: (lens, row of [`twin_rows`]).
pair: Option<(&'static str, usize)>,
}
/// Render ONE lens's thin reading from the JSON its `Mimir.*` RPC returned.
fn render_thin(ui: &mut egui::Ui, view: View, r: &RemoteReports, clicks: &mut Clicks) {
ui.horizontal_wrapped(|ui| {
chip(ui, "thin", TEAL);
ui.label(
egui::RichText::new(
"folded on nornir-server via Mimir.* — the pane renders the server's \
finding-set verbatim (it never folds a warehouse it does not own)",
)
.small(),
);
});
match view {
View::Bloat => {
let v = &r.bloat;
thin_evidence(ui, v);
egui::ScrollArea::vertical()
.max_height(320.0)
.id_salt("thin-bloat")
.show(ui, |ui| {
for a in thin_arr(v, "worst_artifacts") {
ui.label(
egui::RichText::new(format!(
"{} / {} tax {} ({:.1}%) actionable {}",
thin_s(a, "repo"),
thin_s(a, "artifact"),
kb(thin_n(a, "tax_bytes") as u64),
thin_n(a, "tax_pct"),
kb(thin_n(a, "actionable_tax_bytes") as u64),
))
.strong()
.monospace(),
);
for g in thin_arr(a, "top_named").iter().take(6) {
ui.monospace(format!(
" {:>6}x {:>9} [{}] {}",
thin_n(g, "instantiations") as u64,
kb(thin_n(g, "tax_bytes") as u64),
thin_s(g, "crate"),
thin_s(g, "symbol"),
));
}
}
});
}
View::Zombie => {
let v = &r.zombie;
thin_evidence(ui, v);
egui::ScrollArea::vertical()
.max_height(320.0)
.id_salt("thin-zombie")
.show(ui, |ui| {
for f in thin_arr(v, "worst") {
ui.monospace(format!(
"{:<14} {:<24} items {:>4} calls {:>5}",
thin_s(f, "repo"),
thin_s(f, "feature"),
thin_n(f, "gated_items") as u64,
thin_n(f, "inbound_calls") as u64,
));
}
});
}
View::Risk => {
let v = &r.risk;
if let Some(a) = v.get("model_attribution").and_then(|x| x.as_str()) {
ui.label(egui::RichText::new(a).small().italics());
}
thin_evidence(ui, v);
ui.label(
egui::RichText::new(
"The score is a SORT KEY, not a verdict — the three inputs are the columns.",
)
.small()
.strong(),
);
ui.label(
egui::RichText::new(format!(
"{:<10} {:<40} {:>8} {:>8} {:>6} {:>10}",
"REPO", "FILE", "CALLERS", "CHURN90", "COV", "RISK"
))
.small()
.monospace()
.strong(),
);
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-risk")
.show(ui, |ui| {
for u in thin_arr(v, "worst") {
let file = thin_s(u, "file");
let file = if file.len() > 40 {
file[file.len() - 40..].to_string()
} else {
file
};
ui.label(
egui::RichText::new(format!(
"{:<10} {:<40} {:>8} {:>8} {:>5.0}% {:>10.0}",
thin_s(u, "repo"),
file,
thin_n(u, "downstream_callers") as u64,
thin_n(u, "churn_90d") as u64,
thin_n(u, "coverage_frac") * 100.0,
thin_n(u, "risk"),
))
.monospace()
.small(),
);
}
});
}
View::WeakTests => {
let v = &r.weak_tests;
thin_evidence(ui, v);
egui::ScrollArea::vertical()
.max_height(320.0)
.id_salt("thin-weak")
.show(ui, |ui| {
for t in thin_arr(v, "worst") {
ui.monospace(format!(
"{:<12} {:<32} {:<12} runs {:>3} versions {:>2} churn90 {:>4}",
thin_s(t, "repo"),
thin_s(t, "test_name"),
thin_s(t, "verdict"),
thin_n(t, "runs") as u64,
thin_n(t, "versions") as u64,
thin_n(t, "churn_commits_90d") as u64,
));
}
});
}
View::BenchGaps => {
let v = &r.bench_gaps;
thin_evidence(ui, v);
egui::ScrollArea::vertical()
.max_height(320.0)
.id_salt("thin-bench")
.show(ui, |ui| {
for rp in thin_arr(v, "per_repo") {
ui.horizontal(|ui| {
if rp
.get("never_benched")
.and_then(|b| b.as_bool())
.unwrap_or(false)
{
chip(ui, "NEVER BENCHED", RED);
}
ui.monospace(format!(
"{:<12} {:>3} unbenched / {:>3} hot paths · {} run(s)",
thin_s(rp, "repo"),
thin_n(rp, "unbenched") as u64,
thin_n(rp, "hot_paths") as u64,
thin_n(rp, "bench_runs") as u64,
));
});
for h in thin_arr(rp, "worst").iter().take(4) {
ui.label(
egui::RichText::new(format!(
" {:<28} fan-in {:>4} churn90 {:>4} cov {:>4.0}%",
thin_s(h, "module"),
thin_n(h, "fan_in") as u64,
thin_n(h, "commits_90d") as u64,
thin_n(h, "test_coverage") * 100.0,
))
.monospace()
.small(),
);
}
}
});
}
View::SimilarCode => {
let v = &r.similar_code;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.SimilarCode — the relocated dupes query \
(embeddings × symbol_facts × scip_occurrences).",
)
.small(),
);
});
thin_evidence(ui, v);
render_pair_rows(ui, "similar_code", v, clicks);
}
View::SemanticClones => {
let v = &r.semantic_clones;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.SemanticClones — SimilarCode's AND-gate INVERTED: high \
cosine, LOW structural agreement, i.e. independent \
RE-implementations. The two lenses PARTITION the high-cosine \
pair space; a pair is one's finding or the other's, never both.",
)
.small(),
);
});
thin_evidence(ui, v);
// The ranked array is `findings` — NOT SimilarCode's `top_clusters`. Read
// off `SemanticCloneReport::state_json`, because the two folds are twins in
// purpose and NOT in wire shape, and guessing the sibling's key is exactly
// the mistake this comment exists to stop.
egui::ScrollArea::vertical()
.max_height(280.0)
.id_salt("thin-semclones")
.show(ui, |ui| {
let mut pairs_seen = 0usize;
for (ci, c) in thin_arr(v, "findings").iter().enumerate() {
let this_pair = (thin_arr(c, "members").len() >= 2).then(|| {
pairs_seen += 1;
pairs_seen - 1
});
ui.horizontal_wrapped(|ui| {
chip(ui, &format!("score {:.2}", thin_n(c, "score")), TEAL);
if c.get("cross_scope").and_then(|b| b.as_bool()).unwrap_or(false) {
chip(ui, "cross-repo", AMBER);
}
ui.monospace(format!(
"{:<40} ×{} cos {:.2} struct {:.2}",
thin_s(c, "canonical"),
thin_n(c, "size") as u64,
thin_n(c, "avg_cosine"),
thin_n(c, "avg_structural"),
));
if let Some(k) = this_pair {
if ui.link(OPEN_BOTH).clicked() {
clicks.pair = Some(("semantic_clones", k));
}
}
});
for (mi, m) in thin_arr(c, "members").iter().enumerate() {
ui.horizontal(|ui| {
ui.add_space(14.0);
// THE DOOR: a member names its source, and clicking it
// reads the code (`Knowledge.Source` on the member's
// moniker) — the owner, 2026-10-18: "make sure if click
// we see source code or at least its url".
let row = format!(
"{:<10} {}:{} {}::{}",
thin_s(m, "repo"),
thin_s(m, "file"),
thin_n(m, "line") as u64,
thin_s(m, "module"),
thin_s(m, "ident"),
);
if ui
.link(egui::RichText::new(row).monospace().small())
.clicked()
{
clicks.pick = Some((ci, mi));
}
});
}
}
});
}
View::Villa => {
let v = &r.villa;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.Villa — IMPURE / shell-out heresy over the CPG. Every \
std::process::Command, raw exec/system escape and process::exit \
lights up; a site in a known offender names the tool being replaced.",
)
.small(),
);
});
thin_evidence(ui, v);
ui.horizontal_wrapped(|ui| {
for (k, n) in v
.get("by_kind")
.and_then(|b| b.as_object())
.into_iter()
.flatten()
{
let severe = k.as_str() == "external-tool" || k.as_str() == "shell";
chip(ui, &format!("{k} {n}"), if severe { RED } else { AMBER });
}
});
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-villa")
.show(ui, |ui| {
for h in thin_arr(v, "findings") {
ui.horizontal(|ui| {
let kind = thin_s(h, "kind");
chip(
ui,
&kind,
if thin_s(h, "severity") == "high" {
RED
} else {
AMBER
},
);
let tool = h.get("known_tool").and_then(|t| t.as_str()).unwrap_or("?");
ui.monospace(format!(
"{:<10} {:<40} {:<22} [{}]",
thin_s(h, "repo"),
thin_s(h, "loc"),
thin_s(h, "symbol"),
tool,
));
});
}
});
}
View::BinaryDrift => {
let v = &r.binary_drift;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.BinaryDrift — successive Build Thing artifacts of one variant, compared \
symbol by symbol. A change another commit or toolchain explains is listed for \
what it is; other bytes from the SAME commits and toolchain (or an added reach to \
process / exec / network) are unexplained: a non-reproducible build or a \
build-time injection.",
)
.small(),
);
});
thin_evidence(ui, v);
// The ATTESTATION summary — lead with the yes/no verdict + unexplained count.
let attested = v.get("attested").and_then(|b| b.as_bool()).unwrap_or(false);
let has_symbols = v
.get("first_party_symbols")
.and_then(|n| n.as_u64())
.unwrap_or(0)
> 0;
let (color, verdict) = if !has_symbols {
(AMBER, "drift explained: UNKNOWN")
} else if attested {
(TEAL, "drift explained: YES")
} else {
(RED, "drift explained: NO")
};
ui.horizontal_wrapped(|ui| {
chip(ui, verdict, color);
if let Some(a) = v.get("attestation").and_then(|x| x.as_str()) {
ui.label(egui::RichText::new(a).small().italics());
}
});
ui.horizontal_wrapped(|ui| {
chip(
ui,
&format!(
"{} first-party symbols",
thin_n(v, "first_party_symbols") as i64
),
TEAL,
);
chip(
ui,
&format!("{} explained", thin_n(v, "explained") as i64),
TEAL,
);
chip(
ui,
&format!("{} unexplained", thin_n(v, "unexplained") as i64),
if thin_n(v, "unexplained") > 0.0 {
RED
} else {
TEAL
},
);
for (k, n) in v
.get("by_kind")
.and_then(|b| b.as_object())
.into_iter()
.flatten()
{
let severe = k.as_str() == "unexplained-edge" || k.as_str() == "toolchain";
chip(ui, &format!("{k} {n}"), if severe { RED } else { AMBER });
}
});
egui::ScrollArea::vertical()
.max_height(280.0)
.id_salt("thin-binary-drift")
.show(ui, |ui| {
for f in thin_arr(v, "findings") {
ui.horizontal(|ui| {
let kind = thin_s(f, "kind");
chip(
ui,
&kind,
if thin_s(f, "severity") == "high" {
RED
} else {
AMBER
},
);
let callee = f.get("callee").and_then(|c| c.as_str()).unwrap_or("");
// a change between two builds carries its byte delta and what explains it
let delta = match f.get("delta_bytes").and_then(|d| d.as_i64()) {
Some(d) => format!(
"{d:+} B ({})",
f.get("explained_by").and_then(|e| e.as_str()).unwrap_or("?")
),
None => String::new(),
};
ui.monospace(format!(
"{:<10} {:<44} {} {}",
thin_s(f, "repo"),
thin_s(f, "demangled"),
delta,
if callee.is_empty() {
String::new()
} else {
format!("→ {callee}")
},
));
});
}
});
// Honest boundary — say what this CANNOT catch, verbatim from the engine.
if let Some(cc) = v.get("cant_catch").and_then(|c| c.as_array()) {
if !cc.is_empty() {
ui.add_space(4.0);
ui.label(
egui::RichText::new("Cannot catch (heuristic boundary):")
.small()
.strong(),
);
for item in cc {
if let Some(s) = item.as_str() {
ui.label(egui::RichText::new(format!("• {s}")).small().weak());
}
}
}
}
}
View::Quality => {
let card = brokkr_scorecard(r);
ui.horizontal_wrapped(|ui| {
let overall = thin_s(&card, "overall");
// No score while a lens did not answer: the chip says how many, never 0/100.
let score = match card.get("score").and_then(|x| x.as_i64()) {
Some(n) => format!("{n}/100"),
None => format!(
"not scored: {} of {} lens(es) did not answer",
thin_n(&card, "unmeasured") as u64,
thin_arr(&card, "factors").len()
),
};
chip(
ui,
&format!("Brokkr: {overall} ({score})"),
match overall.as_str() {
"heresy" => RED,
"warn" | "unknown" => AMBER,
_ => TEAL,
},
);
ui.label(
egui::RichText::new(
"Software AS A GRAPH, graded by its lenses — one Brokkr criterion per lens. \
The score is a SORT KEY; the factors below are the columns.",
)
.small(),
);
});
if let Some(a) = card.get("attribution").and_then(|x| x.as_str()) {
ui.label(egui::RichText::new(a).small().italics());
}
ui.separator();
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-quality")
.show(ui, |ui| {
for f in thin_arr(&card, "factors") {
ui.horizontal(|ui| {
let st = thin_s(f, "status");
chip(
ui,
&st,
match st.as_str() {
"fail" => RED,
"warn" => AMBER,
NOT_MEASURED => RED,
_ => TEAL,
},
);
// An unmeasured factor has NO count: it shows its lens's state.
let count = match f.get("count").and_then(|c| c.as_u64()) {
Some(n) => format!("{n:>4}"),
None => {
let state = thin_s(f, "state");
format!(
" — [{}]",
if state.is_empty() { "no state" } else { &state }
)
}
};
ui.monospace(format!("{:<34} {count}", thin_s(f, "name")));
});
ui.label(
egui::RichText::new(format!(" {}", thin_s(f, "criterion"))).small(),
);
}
});
}
View::Churn => {
let v = &r.churn;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
let band = thin_s(v, "band");
chip(ui, &format!("{band} filter"), AMBER);
ui.label(
egui::RichText::new(
"Mimir.Churn — a frequency filter over the per-node change-series \
(git_heat_facts). high-pass = volatile hotspots (churn × complexity), \
low-pass = stable core (a stable node that fired is flagged), band-pass \
= a target band. The hotspot is a SORT KEY; frequency and complexity \
are the columns.",
)
.small(),
);
});
thin_evidence(ui, v);
if let Some(a) = v.get("model_attribution").and_then(|x| x.as_str()) {
ui.label(egui::RichText::new(a).small().italics());
}
ui.label(
egui::RichText::new(format!(
"{:<10} {:<40} {:>6} {:>7} {:>6} {:>8}",
"REPO", "FILE", "FREQ", "TOTAL", "CX", "HOTSPOT"
))
.small()
.monospace()
.strong(),
);
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-churn")
.show(ui, |ui| {
for n in thin_arr(v, "worst") {
let file = thin_s(n, "file");
let file = if file.len() > 40 {
file[file.len() - 40..].to_string()
} else {
file
};
ui.horizontal(|ui| {
if n.get("stable_but_changed")
.and_then(|b| b.as_bool())
.unwrap_or(false)
{
chip(ui, "STABLE→CHANGED", RED);
}
ui.label(
egui::RichText::new(format!(
"{:<10} {:<40} {:>6} {:>7} {:>6} {:>8}",
thin_s(n, "repo"),
file,
thin_n(n, "frequency") as u64,
thin_n(n, "commits_total") as u64,
thin_n(n, "complexity") as u64,
thin_n(n, "hotspot") as u64,
))
.monospace()
.small(),
);
});
}
});
}
View::Anomaly => {
let v = &r.anomaly;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.Anomaly — statistical outliers on the change-series: a stable \
node that suddenly churns, a change-rate spike, an anomalously broad \
change wave, and new-coupling candidates (a documented proxy). Each row \
carries baseline vs observed — the z-score is never a lone verdict.",
)
.small(),
);
});
thin_evidence(ui, v);
if let Some(a) = v.get("model_attribution").and_then(|x| x.as_str()) {
ui.label(egui::RichText::new(a).small().italics());
}
let insuf = ju64(v, "insufficient_history");
if insuf > 0 {
ui.label(
egui::RichText::new(format!(
"{insuf} node(s) had too little history to judge — reported, not fabricated."
))
.small()
.italics(),
);
}
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-anomaly")
.show(ui, |ui| {
for a in thin_arr(v, "worst") {
let kind = thin_s(a, "kind");
ui.horizontal(|ui| {
chip(
ui,
&kind,
if kind == "stable-node-churn" || kind == "breadth-outlier" {
RED
} else {
AMBER
},
);
ui.monospace(format!(
"{:<40} baseline {:>6.1} → observed {:>6.0} z {:>5.1}",
{
let s = thin_s(a, "subject");
if s.len() > 40 {
s[s.len() - 40..].to_string()
} else {
s
}
},
thin_n(a, "baseline"),
thin_n(a, "observed"),
thin_n(a, "z"),
));
});
}
});
}
View::WorldImpact => {
let v = &r.world_impact;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.WorldImpact — changes ranked by how far they ripple: semver_diffs \
(the change) × call_edges (world callers) × dep_graph_edges (constellation \
blast radius). A breaking drop in a widely-depended symbol ranks first. \
The world-impact number is a SORT KEY; the two counts are the columns.",
)
.small(),
);
});
thin_evidence(ui, v);
ui.label(
egui::RichText::new(format!(
"{:<26} {:>4} {:>4} {:>4} {:>7} {:>7} {:>8}",
"CRATE", "ADD", "RM", "CHG", "WORLD", "CONST", "IMPACT"
))
.small()
.monospace()
.strong(),
);
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-world")
.show(ui, |ui| {
for c in thin_arr(v, "worst") {
ui.horizontal(|ui| {
if c.get("breaking").and_then(|b| b.as_bool()).unwrap_or(false) {
chip(ui, "BREAKING", RED);
}
ui.label(
egui::RichText::new(format!(
"{:<26} {:>4} {:>4} {:>4} {:>7} {:>7} {:>8}",
thin_s(c, "crate_name"),
thin_n(c, "added_items") as u64,
thin_n(c, "removed_items") as u64,
thin_n(c, "changed_items") as u64,
thin_n(c, "world_caller_count") as u64,
thin_n(c, "constellation_count") as u64,
thin_n(c, "world_impact") as u64,
))
.monospace()
.small(),
);
});
}
});
}
View::Subclone => {
let v = &r.subclone;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.Subclone — SUB-ITEM clone regions of ONE repo's source: shared \
runs SMALLER than the item they sit in, which no chunk-keyed tier can \
state. PER-REPO by construction: with no repo named the server answers \
a refusal that names the askable repos — a refusal, never a clean \
constellation.",
)
.small(),
);
});
thin_evidence(ui, v);
if v.get("scope_required").and_then(|b| b.as_bool()).unwrap_or(false) {
// THE REFUSAL, usable: each repo it names is a CLICK that writes the
// pane's subclone repo filter and re-asks the one lens.
ui.label(egui::RichText::new("name a repo:").strong());
ui.horizontal_wrapped(|ui| {
for repo in thin_arr(v, "repos_available") {
if let Some(name) = repo.as_str() {
if ui
.link(egui::RichText::new(name).monospace())
.clicked()
{
clicks.sub_repo = Some(name.to_string());
}
}
}
});
} else if v.get("scanned").and_then(|b| b.as_bool()).unwrap_or(false) {
ui.label(
egui::RichText::new(format!(
"`{}` — {} file(s) scanned · {} region(s) (from {} group(s), {} before \
subsumption)",
thin_s(v, "repo"),
thin_n(v, "files_scanned") as u64,
thin_n(v, "findings_total") as u64,
thin_n(v, "groups") as u64,
thin_n(v, "groups_before_subsumption") as u64,
))
.small(),
);
egui::ScrollArea::vertical()
.max_height(300.0)
.id_salt("thin-subclone")
.show(ui, |ui| {
// One row per finding — the answer's own fields (file+lines in
// `location`, sites, files, restated_by, damage), clickable
// through to the source like a semantic-clone member.
for (i, f) in thin_arr(v, "worst").iter().enumerate() {
ui.horizontal(|ui| {
if f.get("sub_item").and_then(|b| b.as_bool()).unwrap_or(false) {
chip(ui, "sub-item", TEAL);
}
let row = format!(
"{:<36} {:>3} lines × {} site(s) in {} file(s) restated ×{} damage {:>5.0}",
thin_s(f, "location"),
thin_n(f, "lines") as u64,
thin_n(f, "sites") as u64,
thin_n(f, "file_count") as u64,
thin_n(f, "restated_by") as u64,
thin_n(f, "damage"),
);
if ui
.link(egui::RichText::new(row).monospace().small())
.clicked()
{
clicks.sub_find = Some(i);
}
});
}
});
}
}
View::SemanticTwins => {
let v = &r.semantic_twins;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.SemanticTwins — v1's semantic twins on v2: Type-4 TWINS (one idea \
written twice: close in meaning, far in structure and body) and Type-3 \
COPIES. One row per pair, both members; ⇆ opens both sources side by side.",
)
.small(),
);
});
thin_evidence(ui, v);
render_pair_rows(ui, "semantic_twins", v, clicks);
}
View::GroupTwins => {
let v = &r.group_twins;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.GroupTwins — the same semantic GROUP of statements in two functions \
of ONE repo, found across interleaved lines. PER-REPO by construction: with \
no repo named the server answers a refusal that names the askable repos.",
)
.small(),
);
});
thin_evidence(ui, v);
if v.get("scope_required").and_then(|b| b.as_bool()).unwrap_or(false) {
ui.label(egui::RichText::new("name a repo:").strong());
ui.horizontal_wrapped(|ui| {
for repo in thin_arr(v, "repos_available") {
if let Some(name) = repo.as_str() {
if ui.link(egui::RichText::new(name).monospace()).clicked() {
clicks.gt_repo = Some(name.to_string());
}
}
}
});
} else {
render_pair_rows(ui, "group_twins", v, clicks);
}
}
View::ReimplementsShared => {
let v = &r.reimplements_shared;
ui.horizontal_wrapped(|ui| {
chip(ui, "nornir RPC", TEAL);
ui.label(
egui::RichText::new(
"Mimir.ReimplementsShared — crate A DEPENDS ON crate B and holds a copy or twin \
of a B function: replace A's with a call to B's (B is the library because A \
imports it). One row per function; ⇆ opens both sources side by side.",
)
.small(),
);
});
thin_evidence(ui, v);
render_pair_rows(ui, "reimplements_shared", v, clicks);
}
View::NameCollision => render_rows(ui, &r.name_collision, "thin-name-collision"),
View::DataReach => render_rows(ui, &r.data_reach, "thin-data-reach"),
View::Trust => render_rows(ui, &r.trust, "thin-trust"),
View::SymbolIdentity => render_rows(ui, &r.symbol_identity, "thin-symbol-identity"),
View::UndocApi => render_rows(ui, &r.undoc_api, "thin-undoc-api"),
View::ReachableVuln => render_rows(ui, &r.reachable_vuln, "thin-reachable-vuln"),
View::Belongs => {}
}
}
// ── the twin pairs: every member a pointer to source ────────────────────────────
/// The lenses whose findings are PAIRS the pane draws with both members and
/// opens side by side ([`LensPane::pick_twin_pair`]).
pub const TWIN_LENSES: [&str; 5] = ["semantic_twins", "group_twins", "semantic_clones", "similar_code", "reimplements_shared"];
/// The pair row's click: open both members' source side by side.
pub const OPEN_BOTH: &str = "⇆ open both";
/// One member of a pair, as the lens's answer names it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TwinSide {
pub repo: String,
/// The function: its room path (`korp::esc::escape`), or the span's ident.
pub function: String,
pub file: String,
pub line: u64,
pub end_line: u64,
/// The room's id for it when the answer carries one the room resolves.
pub moniker: Option<String>,
/// A group twin's statement lines (`2-3,5-7,9`); `""` for a whole function.
pub group_lines: String,
}
impl TwinSide {
/// `file:line` — where the member starts.
pub fn at(&self) -> String {
format!("{}:{}", self.file, self.line)
}
/// What `Knowledge.Source` is asked for: by moniker when the answer has one,
/// else by the function's path (scoped to the member's repo by the caller).
fn node(&self) -> crate::graph_source::NodeRef {
crate::graph_source::NodeRef {
label: self.function.clone(),
moniker: self.moniker.clone(),
kind: crate::graph_source::NodeKind::Symbol,
}
}
fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"repo": self.repo, "function": self.function, "file": self.file,
"line": self.line, "end_line": self.end_line, "at": self.at(),
"moniker": self.moniker, "group_lines": self.group_lines,
})
}
}
/// One pair finding: its kind, its similarity, the lens's own `why`, both members.
#[derive(Clone, Debug, PartialEq)]
pub struct TwinRow {
pub kind: String,
pub score: String,
pub why: String,
pub a: TwinSide,
pub b: TwinSide,
}
impl TwinRow {
fn to_json(&self) -> serde_json::Value {
serde_json::json!({ "kind": self.kind, "score": self.score, "why": self.why, "a": self.a.to_json(), "b": self.b.to_json() })
}
}
fn ju(v: &serde_json::Value, k: &str) -> u64 {
v.get(k).and_then(serde_json::Value::as_u64).unwrap_or(0)
}
fn js<'a>(v: &'a serde_json::Value, k: &str) -> &'a str {
v.get(k).and_then(serde_json::Value::as_str).unwrap_or("")
}
/// **The pair rows of a twins-shaped answer** — read off each lens's wire shape
/// (nornir `room_feed::clones::semantic_twins` / `group_twins::envelope` /
/// `SemanticCloneReport` / `DuplicateCluster`), never guessed across lenses. A
/// finding with fewer than two members is no pair and is not drawn as one.
pub fn twin_rows(lens: &str, v: &serde_json::Value) -> Vec<TwinRow> {
let side_placed = |m: &serde_json::Value| TwinSide {
repo: js(m, "repo").to_string(),
function: if js(m, "path").is_empty() { js(m, "name").to_string() } else { js(m, "path").to_string() },
file: js(m, "file").to_string(),
line: ju(m, "line"),
end_line: ju(m, "end_line"),
// the answer's `id` is the STABLE moniker (version `*`), which the room's
// exact lookup does not resolve: ask by path in the member's repo instead
moniker: None,
group_lines: String::new(),
};
match lens {
"semantic_twins" => thin_arr(v, "worst")
.iter()
.filter_map(|f| {
let m = thin_arr(f, "members");
(m.len() >= 2).then(|| TwinRow {
kind: js(f, "kind").to_string(),
score: format!(
"Type-{} · cos {:.3} · struct {:.2}{}",
ju(f, "clone_type"),
thin_n(f, "cosine"),
thin_n(f, "structural"),
f.get("body_similarity").and_then(serde_json::Value::as_f64).map(|b| format!(" · body {b:.2}")).unwrap_or_default()
),
why: js(f, "why").to_string(),
a: side_placed(&m[0]),
b: side_placed(&m[1]),
})
})
.collect(),
"group_twins" => thin_arr(v, "worst")
.iter()
.filter(|f| f.get("a").is_some() && f.get("b").is_some())
.map(|f| {
let side = |m: &serde_json::Value| TwinSide {
repo: js(m, "repo").to_string(),
function: js(m, "ident").to_string(),
file: js(m, "file").to_string(),
line: ju(m, "line"),
end_line: ju(m, "end_line"),
moniker: None,
group_lines: js(m, "group_lines").to_string(),
};
TwinRow {
kind: "group twin".to_string(),
score: if f.get("exact").and_then(serde_json::Value::as_bool).unwrap_or(false) {
format!("exact group body · {} shared k-gram(s), {} rare", ju(f, "shared_kgrams"), ju(f, "rare_shared"))
} else {
format!("cos {:.3} · {} shared k-gram(s), {} rare", thin_n(f, "cosine"), ju(f, "shared_kgrams"), ju(f, "rare_shared"))
},
why: js(f, "why").to_string(),
a: side(&f["a"]),
b: side(&f["b"]),
}
})
.collect(),
"semantic_clones" => thin_arr(v, "findings")
.iter()
.filter_map(|c| {
let m = thin_arr(c, "members");
let side = |m: &serde_json::Value| TwinSide {
repo: js(m, "repo").to_string(),
function: format!("{}::{}", js(m, "module"), js(m, "ident")),
file: js(m, "file").to_string(),
line: ju(m, "line"),
end_line: ju(m, "line"),
moniker: m.get("id").and_then(serde_json::Value::as_str).map(str::to_string),
group_lines: String::new(),
};
(m.len() >= 2).then(|| TwinRow {
kind: "re-implementation".to_string(),
score: format!("score {:.2} · cos {:.2} · struct {:.2}", thin_n(c, "score"), thin_n(c, "avg_cosine"), thin_n(c, "avg_structural")),
why: format!("{} ×{}", js(c, "canonical"), ju(c, "size")),
a: side(&m[0]),
b: side(&m[1]),
})
})
.collect(),
// a = the function to replace, b = the library's one to call
"reimplements_shared" => thin_arr(v, "worst")
.iter()
.filter(|f| f.get("replace").is_some() && f.get("use_instead").is_some())
.map(|f| TwinRow {
kind: format!("call the library ({})", js(f, "fit").replace('_', "-")),
score: format!("sim {:.2} · {} line(s) · {} caller(s)", thin_n(f, "similarity"), ju(&f["gain"], "lines"), ju(&f["gain"], "callers")),
why: js(f, "action").to_string(),
a: side_placed(&f["replace"]),
b: side_placed(&f["use_instead"]),
})
.collect(),
"similar_code" => first_arr(v, &["worst", "top_clusters"])
.iter()
.filter_map(|c| {
let m = thin_arr(c, "members");
let side = |m: &serde_json::Value| TwinSide {
repo: js(m, "repo").to_string(),
function: format!("{}::{}", js(m, "module_path"), js(m, "item_name")),
file: js(m, "file").to_string(),
line: ju(m, "line"),
end_line: ju(m, "line"),
moniker: m.get("id").and_then(serde_json::Value::as_str).map(str::to_string),
group_lines: String::new(),
};
(m.len() >= 2).then(|| TwinRow {
kind: "copy cluster".to_string(),
score: format!("cos {:.3} · struct {:.2} · ×{}", thin_n(c, "avg_cosine"), thin_n(c, "structural"), m.len()),
why: js(c, "recommendation").to_string(),
a: side(&m[0]),
b: side(&m[1]),
})
})
.collect(),
_ => Vec::new(),
}
}
/// **What a twins lens rests on**, in the lens's own fields: semantic_twins'
/// `thresholds`, group_twins' `floor_basis` (the floor's calibration, or that it
/// is fixture-set) and gates. `""` when the answer states none.
pub fn twin_basis(lens: &str, v: &serde_json::Value) -> String {
match lens {
"semantic_twins" => {
let t = &v["thresholds"];
if !t.is_object() {
return String::new();
}
format!(
"twin (Type-4): cosine ≥ {} AND structural ≤ {} AND body MinHash < {} AND mutual kNN k={}; copy (Type-3): cosine ≥ {} AND structural ≥ {}",
t["twin_cosine"], t["twin_structural_ceiling"], t["twin_body_ceiling"], t["mutual_k"], t["copy_cosine"], t["copy_structural_floor"]
)
}
"group_twins" => {
let o = &v["options"];
let gates = if o.is_object() {
format!(
"a twin = one normalised group body, or group-vector cosine ≥ {} with ≥ {} shared role-class {}-gram(s); ",
o["cosine_floor"], o["min_shared_kgrams"], o["kgram"]
)
} else {
String::new()
};
match js(v, "floor_basis") {
"" if gates.is_empty() => String::new(),
"" => gates.trim_end_matches("; ").to_string(),
fb => format!("{gates}floor basis: {fb}"),
}
}
_ => v.get("thresholds").filter(|t| t.is_object()).map(|t| format!("thresholds: {t}")).unwrap_or_default(),
}
}
/// `2-3,5-7,9` → `[(2,3),(5,7),(9,9)]` (group_twins' `ranges` format).
pub fn line_ranges(s: &str) -> Vec<(u64, u64)> {
s.split(',')
.filter_map(|p| {
let p = p.trim();
match p.split_once('-') {
Some((a, b)) => Some((a.trim().parse().ok()?, b.trim().parse().ok()?)),
None => p.parse().ok().map(|n| (n, n)),
}
})
.collect()
}
/// The pair rows of one twins lens: its basis line, then per pair the kind, the
/// similarity, the [`OPEN_BOTH`] click, the lens's `why`, and both members
/// (repo, function, file:line, a group twin's lines). An empty list is drawn as
/// a measured empty ONLY when the lens says so — a refusal or a gap keeps the
/// evidence above as its whole word.
fn render_pair_rows(ui: &mut egui::Ui, lens: &'static str, v: &serde_json::Value, clicks: &mut Clicks) {
let basis = twin_basis(lens, v);
if !basis.is_empty() {
ui.label(egui::RichText::new(format!("basis — {basis}")).small().italics());
}
let rows = twin_rows(lens, v);
if rows.is_empty() {
if LensAnswer::of(v) == LensAnswer::EmptyFact {
ui.label(egui::RichText::new("no pair cleared the gates — a measured empty, per the note above").small());
}
return;
}
egui::ScrollArea::vertical()
.max_height(360.0)
.id_salt(("thin-pairs", lens))
.show(ui, |ui| {
for (i, row) in rows.iter().enumerate() {
ui.horizontal_wrapped(|ui| {
chip(ui, &row.kind, if row.kind == "copy" || row.kind == "copy cluster" { AMBER } else { TEAL });
ui.monospace(&row.score);
if ui.link(OPEN_BOTH).clicked() {
clicks.pair = Some((lens, i));
}
});
if !row.why.is_empty() {
ui.label(egui::RichText::new(&row.why).small());
}
for side in [&row.a, &row.b] {
ui.horizontal(|ui| {
ui.add_space(14.0);
let lines = if side.group_lines.is_empty() { String::new() } else { format!(" lines {}", side.group_lines) };
ui.monospace(format!("{:<10} {} {}{lines}", side.repo, side.function, side.at()));
});
}
ui.add_space(4.0);
}
});
}
/// The renderer for a lens with no view of its own: its evidence first, then
/// each ranked row as its scalar fields, verbatim (the pane adds no numbers).
fn render_rows(ui: &mut egui::Ui, v: &serde_json::Value, salt: &str) {
thin_evidence(ui, v);
egui::ScrollArea::vertical()
.max_height(320.0)
.id_salt(salt)
.show(ui, |ui| {
for row in thin_arr(v, "worst") {
let line = match row.as_object() {
Some(o) => o
.iter()
.filter(|(k, x)| {
!x.is_array() && !x.is_object() && k.as_str() != "evidence_note"
})
.take(8)
.map(|(k, _)| format!("{k}={}", thin_s(row, k).chars().take(80).collect::<String>()).replace('\n', " "))
.collect::<Vec<_>>()
.join(" "),
None => row.to_string(),
};
ui.monospace(line);
}
});
}
// ── find → FIX ─────────────────────────────────────────────────────────────────────
// A lens today is a FINDING (read-only). The find→fix pattern adds the second half: for
// a lens whose finding maps to a concrete remediation, surface the ACTIONABLE next step
// beside the row — the imperative, the exact command/edit, and an honest
// `auto_apply` marker. There is no fix-APPLY seam wired from dwarves-gui yet (the
// dupes engine computes executable refactor plans, but nothing in this UI runs them), so
// every fix says so: `auto_apply` is a TODO the pane never fakes as done. A diagnostic
// lens (churn/anomaly/world-impact) has NO direct code fix and returns an empty list —
// the same honesty the rest of the pane keeps: never a fabricated action.
/// The universal auto-apply state: dwarves-gui has no fix-apply seam wired, so a fix is
/// a SUGGESTION + the exact command, never a silently-applied edit.
const AUTO_APPLY_TODO: &str = "TODO — no fix-apply seam wired in dwarves-gui (the fix is \
a suggestion + the exact command; nothing here executes it)";
/// One actionable remediation derived from ONE lens finding.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Fix {
/// The lens this fix came from (`zombie`, `bloat`, …) — matches [`View::as_str`].
pub lens: &'static str,
/// What the fix targets (the finding, named): `korp / spark41`, `znippy::parse_stream`.
pub subject: String,
/// The imperative — what to DO.
pub action: String,
/// The exact command or edit to apply it (copy-pasteable / precise).
pub command: String,
/// Honest auto-apply state — a TODO (no apply seam) or `N/A` for a security finding
/// that must never be auto-applied.
pub auto_apply: &'static str,
}
impl Fix {
fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"lens": self.lens,
"subject": self.subject,
"action": self.action,
"command": self.command,
"auto_apply": self.auto_apply,
})
}
}
/// The first present array among `keys` (thin and fat state_json name the same finding
/// list differently for a couple of lenses — read whichever is there).
fn first_arr<'a>(v: &'a serde_json::Value, keys: &[&str]) -> &'a [serde_json::Value] {
for k in keys {
if let Some(a) = v.get(k).and_then(|a| a.as_array()) {
return a.as_slice();
}
}
&[]
}
/// **The find→fix derivation — a pure fold over ONE lens's `state_json`.**
///
/// Reads exactly the finding the pane renders (thin RPC value or fat engine value, same
/// shape) and returns the actionable fix per finding. It invents no findings and no
/// numbers — a fix exists only where a finding does. Capped so the fix list mirrors the
/// ranked head the row list shows.
pub fn fixes_for(view: View, v: &serde_json::Value) -> Vec<Fix> {
let cap = 6;
match view {
View::Zombie => first_arr(v, &["worst", "zombies"])
.iter()
.take(cap)
.map(|f| {
let (repo, feat) = (thin_s(f, "repo"), thin_s(f, "feature"));
Fix {
lens: "zombie",
subject: format!("{repo} / {feat}"),
action: "remove the never-built feature and the code it gates".into(),
command: format!(
"# in {repo}/Cargo.toml drop the `{feat}` feature, then delete its \
`#[cfg(feature = \"{feat}\")]` items"
),
auto_apply: AUTO_APPLY_TODO,
}
})
.collect(),
View::Bloat => first_arr(v, &["worst_artifacts"])
.iter()
.flat_map(|a| {
first_arr(a, &["top_named"]).iter().map(|g| {
let (krate, sym, n) =
(thin_s(g, "crate"), thin_s(g, "symbol"), thin_n(g, "instantiations") as u64);
Fix {
lens: "bloat",
subject: format!("{krate}::{sym} ×{n}"),
action: "type-erase the monomorphised generic to recover the tax".into(),
command: format!(
"# split `{sym}`: keep a thin generic wrapper, move the body into a \
non-generic inner fn (one copy instead of {n})"
),
auto_apply: AUTO_APPLY_TODO,
}
})
})
.take(cap)
.collect(),
View::Villa => first_arr(v, &["findings"])
.iter()
.take(cap)
.map(|h| {
let (repo, loc) = (thin_s(h, "repo"), thin_s(h, "loc"));
let tool = h.get("known_tool").and_then(|t| t.as_str()).unwrap_or("the external tool");
Fix {
lens: "villa",
subject: format!("{repo} {loc} → {tool}"),
action: "replace the shell-out with the pure-Rust in-tree equivalent".into(),
command: format!(
"# drop the std::process::Command call to `{tool}`; call the pure-Rust \
crate instead (baremetal — no OS tooling)"
),
auto_apply: AUTO_APPLY_TODO,
}
})
.collect(),
View::WeakTests => first_arr(v, &["worst", "never_red", "weak"])
.iter()
.take(cap)
.map(|t| {
let (repo, name) = (thin_s(t, "repo"), thin_s(t, "test_name"));
Fix {
lens: "weak_tests",
subject: format!("{repo}::{name}"),
action: "make the test able to go RED (assert on the churning path)".into(),
command: format!(
"# add an assertion to `{name}` that fails when its covered code changes \
(a placebo test proves nothing)"
),
auto_apply: AUTO_APPLY_TODO,
}
})
.collect(),
View::Risk => first_arr(v, &["worst"])
.iter()
.filter(|u| thin_n(u, "coverage_frac") < 1.0)
.take(cap)
.map(|u| {
let (repo, file) = (thin_s(u, "repo"), thin_s(u, "file"));
Fix {
lens: "risk",
subject: format!("{repo} {file}"),
action: "raise coverage on the high-risk file (callers × churn × uncovered)".into(),
command: format!("# add tests reaching {file}; it is high-churn, widely-called and under-covered"),
auto_apply: AUTO_APPLY_TODO,
}
})
.collect(),
View::BenchGaps => {
// Thin shape is `{per_repo:[{repo, worst:[{module}]}]}`; fat shape is a bare
// array of per-repo bench objects. Normalise to the per-repo list either way.
let repos: &[serde_json::Value] = if v.is_array() {
v.as_array().map(|a| a.as_slice()).unwrap_or(&[])
} else {
first_arr(v, &["per_repo"])
};
repos
.iter()
.flat_map(|rp| {
let repo = thin_s(rp, "repo");
first_arr(rp, &["worst"]).iter().map(move |h| {
let module = thin_s(h, "module");
Fix {
lens: "bench_gaps",
subject: format!("{repo}::{module}"),
action: "add a heavy bench for the un-benched hot path".into(),
command: format!(
"# add a criterion bench for `{module}` (benches/…); cargo bench -p {repo}"
),
auto_apply: AUTO_APPLY_TODO,
}
})
})
.take(cap)
.collect()
}
View::SimilarCode => {
// The relocated dupes query. The EXECUTABLE fix (absorb → delete → repoint)
// already exists in dwarves::dupes::proposals and renders in the 🧬 Dupes
// pane — so the fix here points at that plan rather than re-deriving it.
let clusters = first_arr(v, &["top_clusters", "clusters"]).len();
if clusters == 0 {
return Vec::new();
}
vec![Fix {
lens: "similar_code",
subject: format!("{clusters} similar-code cluster(s)"),
action: "consume the shared leaf: absorb the unique knowledge, THEN delete the copy, then repoint".into(),
command: "# 🧬 Dupes → REFACTOR PLANS → ▷ Dry-run apply (or drive it: update_json {\"apply\":\"dry-run\"} / {\"apply\":\"execute\"})".into(),
// The ONE lens whose remediation is EXECUTABLE, and now WIRED: the Dupes
// pane runs `dwarves::dupes::apply` over the computed refactor plans —
// dry-run projects the transcript touching nothing, execute drives the
// type-gated absorb→delete→repoint through a recording executor (no fs I/O
// in this build; a real fs executor is the production plug).
auto_apply: "WIRED — dwarves::dupes::apply drives the plan in the 🧬 Dupes pane: DRY-RUN projects the \
absorb→delete→repoint transcript (nothing touched), EXECUTE runs the plan's state machine \
(no filesystem edits in this build). A real fs executor is the production plug.",
}]
}
View::SemanticClones => {
// NO EXECUTABLE FIX, and that is the honest answer rather than a gap.
// SimilarCode's remediation is absorb → delete → repoint because the copies
// ARE the same code. A Type-4 pair is the opposite case: two independent
// implementations of one behaviour, so "delete one" is a DESIGN call about
// which behaviour is canonical — and the lens itself states on every response
// that its thresholds are uncalibrated for the shipped q8 space.
let n = first_arr(v, &["findings"]).len();
if n == 0 {
return Vec::new();
}
vec![Fix {
lens: "semantic_clones",
subject: format!("{n} independent re-implementation(s)"),
action: "READ the pair, pick the canonical behaviour, then consolidate by hand".into(),
command: "# there is no mechanical rewrite for a Type-4 clone — the two sites are different code".into(),
auto_apply: "NEVER — a Type-4 pair is two implementations of ONE behaviour, so which survives \
is a design decision, not a refactor. The thresholds behind the finding are also \
HEURISTIC and uncalibrated for the shipped embedding profile.",
}]
}
View::BinaryDrift => first_arr(v, &["findings"])
.iter()
.take(cap)
.map(|f| {
let (repo, sym) = (thin_s(f, "repo"), thin_s(f, "demangled"));
Fix {
lens: "binary_drift",
subject: format!("{repo} {sym}"),
action: "AUDIT the compile-time injection — do NOT auto-apply".into(),
command: format!(
"# inspect the build.rs / proc-macro / toolchain that emitted `{sym}` (no \
source counterpart); this is a security finding"
),
auto_apply: "N/A — a security finding is never auto-applied; it must be audited by a human",
}
})
.collect(),
View::NameCollision => first_arr(v, &["worst"])
.iter()
.take(cap)
.map(|c| {
let symbol = thin_s(c, "symbol");
let repos: Vec<String> = c
.get("repos")
.and_then(|a| a.as_array())
.map(|a| a.iter().filter_map(|r| r.as_str().map(str::to_string)).collect())
.unwrap_or_default();
Fix {
lens: "name_collision",
subject: format!("{symbol} ({})", repos.join(", ")),
action: "disambiguate the cross-repo path: rename one owner, or extract the shared \
definition to a single leaf both repos depend on".into(),
command: format!(
"# `{symbol}` is defined in {}: give each a distinct qualified path (rename \
one), or hoist it to one edda leaf so the call graph has ONE owner to bind to",
repos.join(" AND ")
),
auto_apply: AUTO_APPLY_TODO,
}
})
.collect(),
// Sub-item clone regions ARE the dedup finding: the fix is the hoist.
View::Subclone => first_arr(v, &["worst"])
.iter()
.take(cap)
.map(|f| Fix {
lens: "subclone",
subject: thin_s(f, "location"),
action: "hoist the shared region into ONE fn and call it from every site".into(),
command: format!(
"# {} lines shared at {} site(s) across {}: extract the region at {} into \
one fn (it is SMALLER than the item it sits in — no whole-item dedup sees it)",
thin_n(f, "lines") as u64,
thin_n(f, "sites") as u64,
thin_s(f, "files"),
thin_s(f, "location"),
),
auto_apply: AUTO_APPLY_TODO,
})
.collect(),
// A twin pair is two implementations of one idea (or a copy across types):
// which survives is a design call, as for semantic_clones.
View::SemanticTwins => {
let n = first_arr(v, &["worst"]).len();
if n == 0 {
return Vec::new();
}
vec![Fix {
lens: "semantic_twins",
subject: format!("{n} twin/copy pair(s)"),
action: "READ both sides (⇆ open both), pick the canonical one, consolidate by hand".into(),
command: "# a Type-4 twin is different code for one behaviour: no mechanical rewrite".into(),
auto_apply: "NEVER — which implementation survives is a design decision, not a refactor.",
}]
}
// A group twin IS a hoistable region: the same statements in two functions.
View::GroupTwins => first_arr(v, &["worst"])
.iter()
.take(cap)
.map(|f| Fix {
lens: "group_twins",
subject: format!("{} ↔ {}", js(&f["a"], "ident"), js(&f["b"], "ident")),
action: "hoist the shared statement group into ONE fn both call".into(),
command: format!(
"# extract lines {} of {} and {} of {} into one fn (the group is interleaved with other lines)",
js(&f["a"], "group_lines"),
js(&f["a"], "file"),
js(&f["b"], "group_lines"),
js(&f["b"], "file"),
),
auto_apply: AUTO_APPLY_TODO,
})
.collect(),
// A re-implemented library: the call to the library replaces the copy.
View::ReimplementsShared => first_arr(v, &["worst"])
.iter()
.take(cap)
.map(|f| Fix {
lens: "reimplements_shared",
subject: format!("{} → {}", js(&f["replace"], "path"), js(&f["use_instead"], "path")),
action: js(f, "action").to_string(),
command: format!(
"# {} lines {}-{} of {}: call `{}` ({})",
js(&f["replace"], "repo"),
ju(&f["replace"], "line"),
ju(&f["replace"], "end_line"),
js(&f["replace"], "file"),
js(&f["use_instead"], "path"),
js(f, "fit").replace('_', "-"),
),
auto_apply: AUTO_APPLY_TODO,
})
.collect(),
// Diagnostic lenses point at WHAT changed / WHAT is risky — there is no single
// code edit that "fixes" them, so they honestly carry no fix.
View::Churn
| View::Anomaly
| View::WorldImpact
| View::Quality
| View::DataReach
| View::Trust
| View::SymbolIdentity
| View::UndocApi
| View::ReachableVuln
| View::Belongs => Vec::new(),
}
}
/// The find→fix contract, as observable metadata (a robot reads the honesty line here).
fn fix_pattern_meta() -> serde_json::Value {
serde_json::json!({
"kind": "find_fix",
"actionable": true,
// The read-only ↔ actionable line, stated plainly and HONESTLY split: the one lens
// with an executable remediation (SimilarCode/Dupes) now has a REAL apply seam;
// every other lens surfaces the fix + the exact command but has no executor, so it
// stays a suggestion. "partial" is the honest state — neither a blanket TODO nor a
// claim that everything auto-applies.
"auto_apply": "partial",
// The lenses whose fix is EXECUTABLE today (the apply seam is wired for them).
"apply_wired_lenses": ["similar_code"],
"apply_seam": "dwarves::dupes::apply (ApplyOutcome::dry_run / ::execute over the computed \
RefactorPlan) — driven from the 🧬 Dupes pane (update_json {\"apply\":\"dry-run\"} \
/ {\"apply\":\"execute\"}).",
"note": "Every lens finding carries its FIX — the imperative + the exact command. Auto-apply \
is PARTIAL: the SimilarCode/Dupes lens now has a real apply seam — dwarves::dupes::apply \
runs the plan (absorb → delete → repoint) as a DRY-RUN (projection, nothing touched) or \
an EXECUTE (the plan's state machine through a recording executor, no filesystem edits in \
this build; a real fs executor is the production plug). Every OTHER lens stays a suggestion \
(no executor), and a security finding (binary_drift) is never auto-applied by design.",
})
}
/// Render the find→fix block for the current view's fixes (both thin and fat paths).
fn render_fixes(ui: &mut egui::Ui, fixes: &[Fix]) {
ui.separator();
ui.horizontal_wrapped(|ui| {
ui.label(egui::RichText::new("🔧 Fix on lens").strong().color(TEAL));
chip(ui, "read-only suggestion", AMBER);
ui.label(
egui::RichText::new(
"the actionable next step per finding — the imperative + the exact command. \
Auto-apply is a TODO (no apply seam); nothing here edits your code.",
)
.small(),
);
});
if fixes.is_empty() {
ui.label(
egui::RichText::new(
"this lens is diagnostic — it names WHAT changed / WHAT is risky; there is no \
single code edit that fixes it, so it honestly carries no fix.",
)
.small()
.weak(),
);
return;
}
egui::ScrollArea::vertical()
.max_height(220.0)
.id_salt("lens-fixes")
.show(ui, |ui| {
for f in fixes {
ui.horizontal_wrapped(|ui| {
let (label, color) = if f.auto_apply.starts_with("N/A") {
("audit only", RED)
} else if f.auto_apply.starts_with("WIRED") {
// The one lens with a real apply seam (SimilarCode/Dupes).
("apply: wired (dry-run/execute)", TEAL)
} else {
("auto-apply: TODO", AMBER)
};
chip(ui, label, color);
ui.label(egui::RichText::new(&f.action).strong());
});
ui.label(
egui::RichText::new(format!(" {}", f.subject))
.small()
.monospace(),
);
ui.label(
egui::RichText::new(format!(" {}", f.command))
.small()
.monospace()
.color(TEAL),
);
}
});
}
impl Facet for LensPane {
fn title(&self) -> &str {
&self.title
}
fn ui(&mut self, ui: &mut egui::Ui) {
self.poll();
// READ ON OPEN. nornir computes each lens on the request from its code
// room (Mímir v2: no chain, no cache — a query, not a job), so making the
// operator press a button to see it is pure friction. Fires once per pane
// (and again after a workspace change, which clears the reading).
if !self.auto_read && self.pending.is_none() && !self.scanned() && self.error.is_none() {
self.auto_read = true;
self.scan();
}
if self.is_pending() {
// Keep the frame ticking while the worker folds; land the result next poll.
ui.ctx()
.request_repaint_after(std::time::Duration::from_millis(120));
}
ui.horizontal(|ui| {
ui.heading("🔍 Mímir");
// NO Scan/⟳ button (owner 2026-08-22: SCAN is not a user action). This
// pane only READS: it reads itself on open (above); a spinner shows while
// that read lands, and the header says, per lens, what the answer rests
// on (`answer_status`: complete / partial / empty / not wired / refused)
// — never a button that makes the operator wait.
//
// There is ONE place to ask the server to catch up, which is NOT here:
// `🧹 Repopulate` in the warehouse picker (`warehouse_select.rs`) drives
// `Workspaces.Repopulate { purge: true }` — a consume for every member
// behind its offset, once, for the whole warehouse. If a lens here says
// `not_consumed`, that button is the answer; a lens that says
// `not_wired` waits for a producer no button can start.
if self.pending.is_some() {
ui.spinner();
}
if self.view == View::Bloat {
ui.checkbox(&mut self.actionable_only, "actionable only");
}
// The find→fix affordance: show the actionable next step per finding.
ui.checkbox(&mut self.show_fixes, "🔧 fixes");
});
ui.label(egui::RichText::new(self.status()).monospace());
ui.label(egui::RichText::new(format!("source: {}", self.source.describe())).small());
ui.separator();
// **The Mímir menu — one place for every code-graph / quality operation.**
// The owner: "alla operationer för att hitta dubbletter och liknande skall ligga
// under Mimir-meny" — dedup (SimilarCode), dead code (Zombie), risk, weak tests,
// bench gaps, the new purity lens (Villa) and the Brokkr quality scorecard are
// ONE grouped menu here, not scattered. Quality is listed first as the summary.
ui.horizontal_wrapped(|ui| {
ui.label(egui::RichText::new("Mímir:").strong().color(TEAL));
for v in [
View::Quality,
View::SimilarCode,
// Its twin sits NEXT to it: the two PARTITION the high-cosine pair
// space, so a reader who finds one should see the other without hunting.
View::SemanticClones,
View::SemanticTwins,
View::Zombie,
View::Risk,
View::WeakTests,
View::BenchGaps,
View::Bloat,
View::Villa,
View::Churn,
View::Anomaly,
View::WorldImpact,
View::BinaryDrift,
View::NameCollision,
View::SymbolIdentity,
View::DataReach,
View::Trust,
View::UndocApi,
View::ReachableVuln,
View::Subclone,
View::GroupTwins,
View::Belongs,
] {
let label = MIMIR_MENU
.iter()
.find(|(view, _)| *view == v.as_str())
.map(|(_, l)| *l)
.unwrap_or(v.as_str());
if ui.selectable_label(self.view == v, label).clicked() {
self.view = v;
}
}
});
ui.add_space(4.0);
if self.view == View::Belongs {
// The L5a decision, on screen — not buried in a module doc.
ui.label(
egui::RichText::new(
"These lenses QUERY the warehouse, and nornir owns the warehouse (L5a). \
The query half of each belongs in nornir behind an RPC so korp's UI and \
nornir's own viz can use it too — a query only dwarves can reach is the \
mistake the dupes engine already made. What stays in dwarves is the GATE \
(--fail-on-findings) and the PROPOSAL.",
)
.small(),
);
ui.add_space(6.0);
The file goes on; the console shows all of it.