crates/nornir-warehouse-embed/proto/mimir.proto
// A TRIMMED copy of nornir-server's `Mimir` service — EVERY lens verb nornir
// answers (the 18 of `mimir_lens::LENSES`, `tests/mimir_verbs.rs` holds the list)
// plus the dependency verbs the forge's release plan reads. Until 2026-10-16 it
// carried 12 lenses, so the forge answered `Unimplemented` for DataReach, Trust,
// NameCollision, SymbolIdentity, UndocApi and ReachableVuln.
// Package `nornir.v1` so the generated method paths (`/nornir.v1.Mimir/ChangeRisk`
// …) match the live `nornir-server` — dwarves speaks it as a THIN client WITHOUT
// linking the `nornir` crate (a `dwarves → nornir` edge would be a dependency
// cycle; the `nornir-funnel-embed` / telemetry / warehouse pattern). Field numbers
// and method signatures are byte-identical to `nornir/proto/nornir.proto` (the
// `Mimir` service + `LensQuery`/`GenericCostQuery`) so the wire is compatible —
// `tests/proto_drift.rs` re-derives that contract from nornir's canonical proto and
// fails if a message or method here drifts from it.
//
// ARCHITECTURE (L5a): the QUERY half of every lens belongs in nornir, which owns
// the warehouse, exposed as an RPC so korp's UI and nornir's own viz can reach it
// too. Each method returns the SAME JSON the lens report's `state_json()` emits
// (the observable surface: ranked findings + an `evidence_note` naming missing
// feeds by table) carried as a string, so the wire stays schema-light. A thin
// dwarves pane RENDERS that returned finding set rather than folding a warehouse it
// does not have.
//
// The `JsonResponse` carrier is the one already declared in `workspaces.proto`
// (same `nornir.v1` package) — imported here rather than re-declared, so the whole
// stub set compiles to ONE FileDescriptorSet without a duplicate symbol.
syntax = "proto3";
package nornir.v1;
import "workspaces.proto";
// Mimir LENSES — a READ over the warehouse returning ranked findings as JSON.
// Every response carries an `evidence_note` naming missing feeds by table; an empty
// ranked list and an unfed table must never draw the same.
service Mimir {
rpc GenericCost (GenericCostQuery) returns (JsonResponse); // symbol_sizes × binary_facts (× scip_occurrences)
rpc ZombieFeatures (LensQuery) returns (JsonResponse); // feature_gate_facts × call_edges × symbol_sizes
rpc ChangeRisk (LensQuery) returns (JsonResponse); // git_heat_facts × call_edges × coverage × semver_diffs
rpc SimilarCode (LensQuery) returns (JsonResponse); // embeddings × symbol_facts (× scip_occurrences)
// SimilarCode's AND-gate INVERTED: high cosine + LOW structural agreement, i.e.
// independent RE-implementations. The two PARTITION the high-cosine pair space —
// a pair is one lens's finding or the other's, never both. Trimmed in 2026-08-25
// when the lens joined the chain; the server verb (nornir.proto:791) predates it.
rpc SemanticClones (LensQuery) returns (JsonResponse); // embeddings × symbol_facts × call_edges — Type-4
rpc WeakTests (LensQuery) returns (JsonResponse); // test_outcomes × git_heat_facts × coverage_fn
rpc BenchGaps (LensQuery) returns (JsonResponse); // git_heat_facts × call_edges × coverage_fn × bench_results
rpc Villa (LensQuery) returns (JsonResponse); // call_edges × symbol_facts — IMPURE/shell-out heresy (std::process::Command, exec/system, external tools)
// The SOURCE-door lens: sub-item clone regions of ONE repo's checkout, folded from
// source text with the code room's item spans (`nornir::mimir_lens::subclone`).
// `repo = ""` is a REFUSAL by design that names the askable repos. nornir's
// `mimir_lens::answer()` dispatches "subclone" like any lens, but the canonical
// nornir.proto carries no `Subclone` verb yet (the MCP tool is its live door) — so a
// live upstream answers `Unimplemented` on this path and the pane shows THAT as the
// one lens's error, never as "no findings". Declared here so the client, the proxy
// forward and the pane row exist the moment the server verb lands.
rpc Subclone (LensQuery) returns (JsonResponse); // source text × room item spans — sub-item clone regions, PER-REPO by construction
// The second SOURCE-door lens (2026-10-20): the same semantic GROUP of statements in two
// functions of ONE repo (`nornir::mimir_lens::group_twins`), PER-REPO like Subclone —
// repo="" answers the refusal that names the askable repos.
rpc GroupTwins (LensQuery) returns (JsonResponse); // source text × room item spans × embeddings_groups — group twins, PER-REPO by construction
// ── the CHANGE / ANOMALY lens family (over the TEMPORAL graph) ───────────
rpc Churn (ChurnQuery) returns (JsonResponse); // git_heat_facts × symbol_facts — frequency filter (high/low/band-pass), churn×complexity hotspot rank
rpc Anomaly (AnomalyQuery) returns (JsonResponse); // git_heat_facts (all snapshots) — z-score/EWMA outliers on the change-series
rpc WorldImpact (LensQuery) returns (JsonResponse); // semver_diffs × call_edges × dep_graph_edges — changes ranked by world + constellation downstream
rpc BinaryDrift (LensQuery) returns (JsonResponse); // symbol_sizes × binary_facts − symbol_facts × call_edges (+ dwarf_* edges): compile-time-injection detector / source↔binary attestation
// ── the six lenses the trim lacked until 2026-10-16 (same wire as nornir.proto) ──
rpc DataReach (LensQuery) returns (JsonResponse); // TOUCHES_TABLE × reversed CALLS: which code reaches which warehouse table
rpc Trust (TrustQuery) returns (JsonResponse); // bottleneck trust from the entry surface: act / hold / refuse
rpc NameCollision (LensQuery) returns (JsonResponse); // CALLS landing on merged ids vs the leaf-name baseline
rpc SymbolIdentity (LensQuery) returns (JsonResponse); // own definitions whose id the fold merged, by cause
rpc UndocApi (UndocApiQuery) returns (JsonResponse); // undocumented public API ranked by churn
rpc ReachableVuln (LensQuery) returns (JsonResponse); // advisories × sbom, refuse only when own code CALLS the crate
// ── the clone signals (2026-10-17, same wire as nornir.proto): clones by what a function DOES ──
rpc ExactClones (LensQuery) returns (JsonResponse); // the same normalised body (names → roles)
rpc NearClones (LensQuery) returns (JsonResponse); // MinHash/LSH over the normalised bodies
rpc StructuralClones (LensQuery) returns (JsonResponse); // IDF-weighted types / fields / callees
rpc RoleNeighbours (LensQuery) returns (JsonResponse); // FastRP role vectors, nearest by role
rpc SameIntent (LensQuery) returns (JsonResponse); // raw-text model, NAME-DRIVEN
// The twins × DEPENDS_ON (2026-10-22): crate A imports crate B and holds a copy or twin
// of a B function — call the library's (`nornir::mimir_lens::room_feed::reuse`).
rpc ReimplementsShared (LensQuery) returns (JsonResponse); // semantic_twins + similar_code pairs × DEPENDS_ON × CALLS
rpc SemanticTwins (LensQuery) returns (JsonResponse); // Type-3 copies (cos ≥ 0.92 AND structural ≥ 0.50) + Type-4 twins (cos ≥ 0.882 AND structural ≤ 0.35, mutual kNN k=8)
// ── the cross-repo DEPENDENCY verbs the 🚀 Mímir release plan is read from ──
// (2026-10-15). The forge's `MimirRelease.Plan` job asks these of the embedded
// nornir — the release order, each repo's dependencies, what moved since the last
// recorded release and what that affects. Same wire as nornir.proto's `Mimir`.
rpc DepsOf (DepQuery) returns (JsonResponse);
rpc DependentsOf (DepQuery) returns (JsonResponse);
rpc AffectedByChange (AffectedQuery) returns (JsonResponse);
rpc BuildOrder (Empty) returns (JsonResponse);
rpc ChangedSinceLastRelease (Empty) returns (JsonResponse);
}
message DepQuery { string repo = 1; bool transitive = 2; }
message AffectedQuery { repeated string repos = 1; }
// A Mimir-lens query: optional `repo` filter (""=all repos) + a result `limit`
// (0 = a per-lens default).
message LensQuery { string repo = 1; uint32 limit = 2; }
// GenericCost also takes the instantiation floor below which a generic is not
// bloat (0 = default of 2).
message GenericCostQuery { string repo = 1; uint32 min_instantiations = 2; uint32 limit = 3; }
// Churn frequency-filter query: `band` is "high"/"low"/"band"; `cutoff_low`/`cutoff_high`
// are change-frequency (commits_90d) thresholds.
message ChurnQuery { string repo = 1; string band = 2; int64 cutoff_low = 3; int64 cutoff_high = 4; uint32 limit = 5; }
// Anomaly query: `z_threshold` 0 = the 3.0 (three-sigma) default.
message AnomalyQuery { string repo = 1; double z_threshold = 2; uint32 limit = 3; }
// Trust query: `act_at` / `hold_at` 0 = the server's bounds.
message TrustQuery { string repo = 1; double act_at = 2; double hold_at = 3; uint32 limit = 4; }
// UndocApi query: `min_commits_90d` 0 = the server's floor.
message UndocApiQuery { string repo = 1; int64 min_commits_90d = 2; uint32 limit = 3; }