Mímir v2 — land (integration branch mimir-v2-land)
2026-10-13. Spec: workspace_nordisk/PLAN-MIMIR-V2-2026-10-12.md. Inventory of the removal: .nornir/mimir-v2-inventory.md (§8–§10 = what answers not_wired today).
This branch is the first tree where the old roads are gone AND the new engine is present. Nothing is wired yet: every surface in §6 still answers not_wired. No merge to main (plan §7: deletion + rebuild land as one).
| input | head | what it brings |
mimir-v2 (removal lane) |
60ba98c |
roads a–e deleted (−34k lines), guard tests/no_second_parser.rs, every reader of the old tables answers not_wired, gates fail closed |
origin/mimir-v2-room |
067b787 |
crates/mimir-schema, mimir-driver (nightly-2026-10-05, excluded), mimir-ingest, mimir-binary, graphblas-sys, mimir-graph, mimir-room |
Merge base c18e6d1 (nornir main).
1. Merge: conflicts and resolutions
git merge origin/mimir-v2-room on mimir-v2: 2 conflicted files, 3 hunks. Outside the new crate directories the room branch changed only Cargo.toml and src/coderoom.rs.
| # | file | conflict | resolution |
| 1 | Cargo.toml [workspace] |
removal: members unchanged, exclude = ["bench-corpus"] + its comment DELETED (bench-corpus/ is gone). room: 6 new members, exclude = ["bench-corpus", "crates/mimir-driver"] |
members = the removal's list + crates/mimir-schema, mimir-ingest, mimir-binary, graphblas-sys, mimir-graph, mimir-room. exclude = ["crates/mimir-driver"] ONLY — bench-corpus and its comment stay deleted. The driver comment is kept and names the guard. |
| 2 | src/coderoom.rs hunk 1 (~1 480 lines) |
removal DELETED the tarball scan (definition_extents, walk_extents, ingest, compat edge, what-breaks, snapshot-diff). room MODIFIED one line inside it (walk_extents: impl scope Type# → impl#[Type] via mimir_schema::moniker::impl_descriptor) |
the region stays DELETED. The room's edit was to the syn walker; with the walker gone there is nothing to carry. The RA-form impl scope lives on where it is written now: mimir-schema (formatter) and mimir-driver (the one producer). |
| 3 | src/coderoom.rs hunk 2 (tests, ~130 lines) |
removal DELETED the tests over definition_extents. room RENAMED/REWROTE one of them (an_impl_method_scope_is_rust_analyzers_impl_scope) |
stays DELETED — it tests the deleted walker. The moniker bytes are pinned by mimir-schema's own tests and by the driver's fixture (21/21 symbols byte-identical to rust-analyzer scip). |
Auto-merged in src/coderoom.rs, and KEPT (the part of the room's change that touches surviving code): scip_descriptor → mimir_schema::moniker::descriptor_for_kind, scip_moniker → mimir_schema::moniker::moniker, the ItemExtent::scope doc (impl#[B]), and root Cargo.toml's mimir-schema dependency. One writer of moniker bytes (LAW 5).
Follow-ups of the resolution, in the merge commit:
escape_scip_name(the room made itpub(crate)forwalk_extents) has NO caller left after hunk 1 → removed, not kept unused.scip_descriptoris private again.- one stale comment line (
Type#method().→impl#[Type]method().).
Check that nothing deleted came back: the 62 paths git diff --diff-filter=D c18e6d1 mimir-v2 lists — none exists in the merged tree.
2. Guard (tests/no_second_parser.rs)
The guard iterates [workspace].members, so the six new member crates were covered the moment they became members. Made explicit and widened:
| rule | what was added |
| 7 | every crate directory on disk (crates/*, top-level dirs with a Cargo.toml) is a workspace member or THE excluded driver; [workspace].exclude is exactly ["crates/mimir-driver"]; the six stable Mímir v2 crates are members BY NAME (MIMIR_V2_MEMBERS) and among the scanned manifests. For member crates the source scan now reads src/, tests/, examples/, benches/ and build.rs (was src/ only). |
| 8 | crates/mimir-driver is the ONE user of compiler internals (COMPILER_INTERNALS_USER): it must really name rustc_private (no stale exemption), carry its own [workspace] table and a DATED nightly pin (nightly-YYYY-MM-DD); its manifest and sources are scanned for the forbidden crates like everyone else; no member source names rustc_private / extern crate rustc_*, no member manifest asks for it, no member pins a toolchain. |
Not forbidden, noted: graphblas-sys has an OPTIONAL build-dependency bindgen (feature regenerate, off by default; bindings are checked in). bindgen parses C headers, not Rust; it pulls syn transitively the way any proc-macro crate does (plan §7.1: not ours).
Result and red-proof: §4.
3. What is NOT in this branch
- No wiring (§6 is the map, no code).
mimir-driveris not built by anything in the workspace; it is built by hand on its nightly (cd crates/mimir-driver && cargo build), see lane A in §6.- The root crate depends on
mimir-schemaonly. It does not link GraphBLAS yet.
4. Compile and test matrix
Box: dwarf (12 threads, load 25–95 during the runs — shared), stable rustc 1.98.1, siblings from ~/d-scip, Cargo.lock seeded from the room worktree, --offline. Logs: ~/d-scip/tmp/land-logs/. Removal baseline: the mimir-v2-L2 row in nornir-workspace.toml and the removal lane's logs (~/d-scip/tmp/mimirv2/).
| step | command | removal lane (mimir-v2) |
land |
| default | cargo check |
green | green (11m28s cold). nornir lib 46 warnings, none in src/coderoom.rs — the only source file that differs from the removal tree |
| full-server | cargo check --no-default-features --features full-server |
green | green (nornir lib 15 warnings) |
| test targets | cargo check --tests --keep-going |
RED, same as main: BenchRun lacks id / machine_facts in the fixtures, Toolchain lacks image / image_sha256 — bin nornir (test), cli_ui_parity, data_submit_rpcs, mcp_matrix, mcp_tools, release_gates, viz_surface |
RED in the SAME 7 targets with the same 7 × E0063, nothing else |
| whole workspace | cargo check --workspace |
(the six crates were not there) | green; no warning from any of the six new crates |
| guard | cargo test --test no_second_parser |
5 tests green | 7 tests green |
| lib unit tests | cargo test --lib |
1046 passed / 0 failed at a9635e7; two later removal commits deleted more code with its tests |
995 passed, 0 failed, 16 ignored (275 s). Same test set as the removal head 60ba98c by construction: git diff mimir-v2 -- src touches no test, and both trees hold 1 102 test attributes under src/ (a9635e7: 1 157) |
New crates (cargo test -p … --no-fail-fast, one run, 466 s):
| crate | tests | result |
mimir-schema |
10 integration | green |
mimir-ingest |
13 unit + 28 integration (git walk, checkout, consumer, owners, binary, real driver) | green |
mimir-binary |
14 unit + 1 fixture binary | green |
graphblas-sys |
none (links libgraphblas 7.4.0) |
builds |
nornir-mimir-graph |
20 ops + 1 proptest | green |
nornir-mimir-room |
10 (182 s) | green |
mimir-driver — its own workspace, nightly-2026-10-05, cd crates/mimir-driver && cargo test |
1 unit + 3 fixture dump + 2 pin | green |
Guard red-proof (the compiled guard binary re-run against the edited tree, each edit reverted, then green again — log guard-red.log):
| edit | test that went red |
syn = "2" in crates/mimir-room/Cargo.toml [dependencies] |
no_manifest_in_the_workspace_declares_a_second_parser_or_graph_engine |
use syn::visit::Visit; in crates/mimir-room/benches/zz_redproof.rs |
no_source_uses_syn_or_spawns_rust_analyzer |
#![feature(rustc_private)] + extern crate rustc_middle; in the same file |
mimir_driver_is_the_one_user_of_compiler_internals |
crates/zz-stray/Cargo.toml on disk, not a member |
every_crate_on_disk_is_a_member_or_the_one_excluded_driver |
Not run here: --all-features (red on main and on the removal lane: gpu.rs wgpu API), and the root crate's integration tests (the removal lane runs those against its own head; this merge changes none of them except the guard).
5. libgraphblas-dev
graphblas-sys links the system SuiteSparse:GraphBLAS dynamically (cargo:rustc-link-lib=dylib=graphblas). Its build.rs only WARNS when the header is missing, so nothing fails until a LINK happens: cargo check stays green on a box without the library; cargo build / cargo test of anything that links mimir-graph / mimir-room fails at link time (-lgraphblas).
Today only --workspace (or -p) commands reach those crates, because the root crate does not depend on them yet.
| gate / step | command | without libgraphblas-dev |
release TEST gate — release::pipeline::run_cargo_test (src/release/pipeline.rs:650) |
cargo test --workspace --no-fail-fast -j 1 |
RED: link error in nornir-mimir-graph + nornir-mimir-room test binaries, mimir-room bin, graphblas-sys |
build gate — edda nornir-build-thing buildgate.rs:961–963 |
cargo test --workspace --no-run --tests, cargo build --workspace --bins |
RED (same targets) |
target check — edda nornir-testmatrix targetcheck.rs |
cargo build --workspace --tests --keep-going |
RED rows for the mimir-graph / mimir-room targets (cargo check --workspace --all-targets arm stays green) |
test surface — edda nornir-testmatrix testsurface.rs:748 |
cargo test --workspace |
RED |
CI .woodpecker.yml build-test |
cargo build --workspace, cargo test --workspace |
RED (apt list has no libgraphblas-dev) |
edition gate — edda nornir-release-core edition.rs:333/375 |
cargo check --workspace |
green (no link; a cargo:warning) |
stage rehearsal — release/stage.rs:628 |
cargo publish --workspace --no-verify |
no link, so not tripped by the library. Separate finding: it would now try to publish the five publishable mimir crates, and mimir-ingest depends on mimir-binary (publish = false) — NOT verified here; decide publish = false on all mimir crates until landing |
edition gate, opt-in docs rehearsal — edition.rs:403 |
cargo doc --workspace --all-features |
trips on EVERY box without libclang-dev, library or not: --all-features switches on graphblas-sys/regenerate (bindgen at build time) |
After lane A/B make the root crate depend on mimir-room, every gate that LINKS the root crate trips too (cargo test -p nornir … lines in CI, xtask feature-matrix builds, tests/atom_linkage.rs, the solo tests), and nornir-server / dwarves-full gain a RUNTIME dependency on libgraphblas.so.7 (+ libgomp).
Proposed fix (not implemented): a build requirement, not a feature flag — plus one narrow feature for linkage.
- GraphBLAS is THE engine (plan §4), not an optional extra. A flag that lets the workspace build without it creates a second "engine absent" state on every surface and a new matrix dimension — the shape §7 forbids. And a root-crate feature would not help the gates above anyway: they say
--workspace, which builds every member regardless of root features. - So: add
libgraphblas-devwhere builds link —.woodpecker.ymlapt list (check the image's package is 7.x:graphblas-sysrefuses another major), thenordisk-cargo-toolchainimage /cargo-test.Containerfile(eddanornir-build-thing), README build prerequisites — andlibgraphblas7in every runtime image / appliance compose that shipsnornir-serverordwarves-full(ops kits: both gunnar and holger). - One preflight line instead of a linker wall: the gates that link (
run_cargo_test, the build gate) probe for the library first and fail withSuiteSparse:GraphBLAS 7.x not found — apt-get install libgraphblas-dev. - The ONE feature gate that buys something: the root crate's dependency on
mimir-roomis optional (code-room), ON inserver/full-server/solo, OFF in the thin atoms (nornir-clithin,nornir-ui, wasm). Thin clients ask the server over gRPC and must not link the engine (tests/atom_linkage.rsalready audits thatnornir-uicarries no server symbols). That gates LINKAGE of thin atoms; it is not a way to run a server without the engine. - Take
regenerateout of cargo features (an env var read bybuild.rs, or an xtask verb) so--all-featuresnever runs bindgen.
Owner decision inside this: dynamic system library (above) vs a vendored static build of GraphBLAS for self-contained sold media (cmake, minutes of build, no package on the target).
6. Wiring map (for the next lanes — no code here)
6.1 The handle every lane uses
One log per workspace, one consumer writing it, rooms opened from it:
<registry_root>/<ws>/git/<member>/ server's clone (monitor::fetch_workspace) — KEPT
<registry_root>/<ws>/builds/mimir_v2/lake/ skade lake, namespace mimir_v2 (EventLog) — NEW
<registry_root>/<ws>/builds/mimir_v2/clones/<member>/ the consumer's own clone (RepoSpec.worktree)
<registry_root>/<ws>/builds/mimir_v2/target/<member>/ nightly target dir (DriverDumper.target_dir)
<registry_root>/<ws>/builds/mimir_v2/out/<member>/ driver IPC dumps (DriverDumper.out_root)
<registry_root>/<ws>/builds/mimir_v2/open-set.bin open-set cache (Consumer::boot)
The lake is its OWN skade catalog, not builds/catalog.redb: redb is single-writer and IcebergWarehouse holds that file; plan §7.5 wants a new namespace nobody else reads. (Decision D1.)
6.2 Where the deleted poll loop + populate are replaced (lane A)
deleted at c18e6d1 |
replaced by |
server_grpc.rs::spawn_server_poll_loop + its call in run_with_policy (the gap right after eprintln!("nornir-server: listening on {addr}"), now ~server_grpc.rs:5763) |
spawn the consumer loop at the same place, same shape (tokio interval, MissedTickBehavior::Skip, body on spawn_blocking) |
server_grpc.rs::server_sweep |
per workspace: reg.list() → Mode::Monitored → nornir_poll_cadence due check (as before) → fetch_served (KEPT, server_grpc.rs:5492) → clone_events::record_sweep_report (KEPT) → for every member that is behind → Consumer::consume(&RepoSpec) inside ctx.work_gate |
workspace_needs_build (current_snapshot empty) and monitor::knowledge_is_gone |
"behind on its offset": EventLog::offsets() has no row for the member, or offset.sha != MemberState.last_seen_sha. Boot, a wiped lake and "a week away" are the same path (plan §3) |
republish_served / republish_served_locked → monitor::republish_with steps 1–9 |
Consumer::step (classify → checkout_changed → Dumper::dump → delta vs open set → ONE atomic release: events + offset + ledger). reg.set_snapshot(ws, …) gets the log's Stamp (or repo@seq list) |
ctx.lens_chain.submit_feeds / submit (re-queue 13 lenses per republish) |
nothing — no result cache. Optional warm: Room::at(log, &Scope::current()) after a batch |
monitor::PhaseJobs (job children per populate phase) |
one job row per (member, commit) from CommitReport + PhaseMs (needs I2). Job kinds live in edda (jera::jobs::kind): reuse WORKSPACE_REPUBLISH as the parent so no cross-repo change is needed |
monitor::sync_once / monitor::spawn_poll_loop (CLI / fat mode) |
nornir workspace populate runs the same per-workspace consume inline |
Workspaces.Register eager build (register, server_grpc.rs:4524, the block at ~4720–4740) and Workspaces.Fetch{force} (fetch, 4777, ~4809) |
call the same consume; the "populate": NOT_WIRED_NOTE job detail becomes the commit reports |
Workspaces.Repopulate (repopulate, 4910) |
same wire type: purged + dropped = the v1 tables dropped once (plan §7.5, I6); generation = EventLog::identity() / stamp; queued = members behind; in_flight = member being consumed; refusal = driver not installed / lake not open |
Ops.ScipIngest (scip_ingest, 5344) |
decision D4: delete the verb (edda nornir-wire proto change) or map it to "consume this member now" |
| status | EventLog::offsets(), runs(repo), bin_runs(repo), Room::stats() — carried on EXISTING wires (Viz.Jobs ledger, the Repopulate report, workspace status JSON). A new RPC is an edda nornir-wire change: avoid in the first pass |
Registry → repos → offsets:
Registry::list() → Workspace{name, mode, poll, members: Vec<MemberState{name, remote, git_ref, last_seen_sha}>}
→ RepoSpec { name: member.name,
source: <root>/<ws>/git/<name> (local path; the fetch keeps auth + HTTPS in ONE place)
branch: member.git_ref or "main",
worktree: <root>/<ws>/builds/mimir_v2/clones/<name>,
initial_offset: D2 }
→ offsets live in mimir_v2.consumer_offsets ONLY (never copied into the registry)
Non-graph feeds that rode the deleted populate and now have NO trigger (P7): they need a side-step on consume, or their lenses stay blind although nothing about them changed — git_heat_facts (knowledge::git_heat::scan_repo, today only release::pipeline::run_pipeline writes it), the sbom / vuln scan, the dep-graph snapshot, the tantivy index, embeddings (monitor::embed_workspace is kept, untriggered).
6.3 What the room API lacks
Read side (crates/mimir-room):
| id | missing | who needs it |
| R1 | node attributes: name, parent, file, is_test, ty, cfg — NodeView has moniker/kind/level/crate/version/repo/origin/provenance only (NODE_READ skips the rest) |
every reader that prints a symbol row; test inventory; file-keyed lenses |
| R2 | spans: code_span_events is not folded at all — node span (file, start, end), edge sites (call-site lines), and file → nodes |
Knowledge rows, defined_in, villa sites, subclone / chunker / embeddings, funnel anchors |
| R3 | name → node: exact / path suffix (a::b::c) / substring, kind filter, limit; and linker symbol → node. Room::ordinal takes an exact moniker only |
SymbolLookup, Callers/Callees by name, funnel retriever, Introspect / Dwarf |
| R4 | bounded reverse walk: dependents_by has no max_hops (the private walk already takes one; forward reach exposes it) |
direct callers, villa, reachable_vuln |
| R5 | seeds as a Mask / ordinals — reach / dependents* take &[&str] monikers only |
every "from this crate / these tests" walk |
| R6 | a path A → B along kinds, and forward hop layers from seeds (min_hops_to goes TO a target set) |
CallPath, PathBetween, metro trace |
| R7 | edge export (from, to, count) of a kind inside a mask, capped; the matrices are boolean, the count column is not read |
callgraph pane, edge-level reports |
| R8 | quotient: collapse nodes by crate / module (CONTAINS ancestor) / repo / file → group×group edge counts + per-group node counts and degrees | arch board, Viz.Knowledge bubbles, release deep edges, bench_gaps, churn complexity, world_impact |
| R9 | the binary stream's symbol rows (size, section, role, artifact, demangled) — the room keeps one flag (linked) |
generic_cost, binary_drift |
| R10 | duplicate keys: which nodes had key_dup / distinct payload hashes (the ledger counts them, the fold merges them) |
name_collision, symbol_identity |
| R11 | semirings beyond boolean reach: (max, min) bottleneck over node weights; mimir-graph::ops has reach / min_hops / fan / similarity |
trust |
| R12 | Answer / Evidence are not Serialize and have no machine state (complete / partial / empty-fact) |
every JSON front door (adapter otherwise hand-maps to feeds + evidence_note) |
| R13 | handle model: Room::open(&EventLog, …), and the EventLog is OWNED by the Consumer; skade Warehouse is single-process and not Clone. No room can open while a consume runs (minutes), and no second process (CLI fat mode, MCP stdio) can read a lake the server holds |
ALL readers — the first thing lane A must settle (shared read handle + read-only open of a held lake, the IcebergWarehouse::open_read_only catalog-copy shape) |
| R14 | sync entry: Room::open is async; viz / CLI / MCP call sites are sync |
viz panes, CLI |
| R15 | typed "repo has no consumer offset" — Scope::resolve returns an anyhow string |
readers must answer not_consumed, not an internal error |
Producer / schema gaps (a surface stays not_wired until its row here closes; none is a room API question):
| id | gap | blocks |
| P1 | CLOSED for visibility + docs by lane D (mimir-v2-wire-d, mimir-schema 2): code_nodes carries visibility (nominal, as rustc resolves it: pub / pub(crate) / pub(in crate::a::b) / private), effective_visibility (rustc's effective visibility: direct / reexported / reachable / reachable_impl_trait / internal), doc (first line) and has_doc; all four are payload (a change is one node event). CLOSED for attributes by lane E (mimir-schema 3): ignored (#[ignore] on a test fn; null on a node that is not a test) and doc_hidden (the item ITSELF carries #[doc(hidden)]; null where has_doc is null and on a cfg_gated_out stub) — nullable booleans, payload. STILL OPEN: a rendered signature (params are PART nodes with ty; the return type is not there), a doc-line count |
undoc_api, autonom surface, release doctor API-surface delta, Knowledge symbol rows (visibility), data_reach public entry points: the producer side is done, they read the columns once R1 exposes them. Test is_heavy = ignored (wired, §9); undoc_api skips doc_hidden (own attribute or a hidden module above it) |
| P2 | CLOSED by lane D, for every cfg that gates CODE: feature nodes (kind feature, one per crate@version feature, defined_here = the manifest declares it); ENABLES (feature → feature / dependency crate / dependency feature, and crate → every feature it was COMPILED with) and ENABLES_WEAK (x?/f); GATED_BY item → feature with negated and partial. partial = false: the item's own #[cfg] — from rustc's CfgTrace for the items it kept, and from a token scan (mimir-driver/src/cfg_scan.rs, proc-macro2, allow-listed in the guard) for the items it compiled out, which are cfg_gated_out STUB nodes (name, file, line, predicate, written visibility; nothing resolved, no CALLS / USES_TYPE). partial = true: a cfg on a statement / expression / match arm / use / macro call (cfg_if!) or a cfg!(…) INSIDE the item (token scan, both configurations). LOWER BOUND that stays, with its size in every dump's stats (feature_cfg_mentions − feature_cfg_attributed − feature_cfg_partial): the predicate of cfg_attr (it switches an attribute, not code) is no edge; a cfg written by a macro with a $metavariable feature name is an edge only on the kept side; the token scan runs on the workspace's own crates (MIMIR_CFG_SCAN=all adds dependencies), so a dependency has own-gate edges of KEPT items only |
zombie_features: NeverBuilt = a declared feature with no ENABLES from a crate node (exact); gated code = GATED_BY sources (partial tells an item that exists only under the feature from one that merely contains gated code); a stub has no inbound edges by construction, so it is evidence for never-built, NEVER for unreached. Knowledge feature-gates: both configurations. Both read cfg / the edges once R1 and the lens are wired |
| P3 | CLOSED by lane K (mimir-v2-wire-k, mimir-schema 4, §10): the driver writes TOUCHES_TABLE (fn → table node, access read / write / both, sites) where a table name meets a read / write of the storage engine; a runtime name is an edge to the one <unresolved> table node. STILL OPEN: calls through &dyn Warehouse made inside a crate that sees no impl (edda's release ledger and knowledge view) — they need the annotation on the trait (§10) |
data_reach, the table-lineage template, the board's Table nodes and the metro termini read it (§10); trust's access docking does not yet |
| P4 | binary CALL edges have no producer (DWARF and LLVM call graphs deleted; mimir-binary writes symbols + INSTANCE_OF) |
binary_drift edge half; Introspect / Dwarf verbs can only answer "source CALLS between LINKED nodes" |
| P5 | world ring callers, COMPATIBLE, semver_diffs: the coderoom ingest is deleted |
world_impact world half, coderoom compat / what-breaks |
| P6 | operator / closure / fn-pointer calls are not recorded (lane 3 open item) | every reachability answer is a LOWER bound — the evidence must say so |
| P7 | non-graph feeds without a trigger (§6.2) | change_risk, churn, anomaly, undoc_api, bench_gaps, weak_tests, reachable_vuln |
Write side (crates/mimir-ingest):
| id | missing | who needs it |
| I1 | one Consumer owns ONE Dumper, and DriverDumper is per repo (repo, target_dir, out_root, pending, mirror). A workspace has N members on ONE log |
the consumer loop |
| I2 | consume() reports only when it returns: no per-commit progress sink, no max_commits bound, no cancel |
job ledger rows, fairness between members, shutdown |
| I3 | CLOSED (lane A: the dry-run delta; lane E: the release doctor asks for it, §9) | release doctor semantic blast |
| I4 | first offset: initial_offset: None replays from the ROOT commit. The field exists — the policy is decision D2 |
first registration of a big repo |
| I5 | nothing probes cargo-mimir-dump + the nightly before the first commit |
Repopulate refusal, honest "driver not installed" |
| I6 | dropping the v1 tables (plan §7.5) is not in ingest — it is an IcebergWarehouse verb |
Repopulate purged / dropped |
6.4 Lane B — the lenses (src/mimir_lens)
Mimir.* RPCs, the MCP mimir_* tools and nornir mimir analyze all reach the same 18 lenses through mimir_lens::answer (+ subclone, MCP only). Wired on mimir-v2-wire-b (the two vector lenses on mimir-v2-wire-j; only data_reach still answers not_wired): the scoring / report halves are kept, their inputs are typed facts read from the code room by src/mimir_lens/room_feed (and the warehouse for the feeds that are not a graph). Every answer is the lens envelope with lens_meta.state = complete | partial | empty_fact | empty_gap, or a refusal state (not_wired, not_consumed, no_room, lake_unavailable, not_linked).
| lens | state | kept half | room feed (as wired) | still missing — said in the answer |
anomaly |
live | whole (anomaly::analyze over change_series) |
none — git_heat_facts, every generation |
a file needs 3 scans for a baseline |
villa |
live | classify re-keyed to the resolved callee path + node kind, known_tools, report |
name lookup of the impure std / libc / nix items → edges(CALLS, to = them) → one finding per positioned site |
P6 |
zombie_features |
live | Unreached / NeverBuilt split, ManifestFacts, features_in_cfg (cross-check of cfg strings against the edges) |
feature nodes; built = ENABLES from a crate node; GATED_BY with negated / partial from EdgeOut; inbound = CALLS + USES_TYPE sites; a cfg_gated_out stub is evidence for never-built only |
lower bounds of P2 (cfg_attr, $metavariable, dependencies = kept items' own gates), P6 |
change_risk |
live | callers × churn × (1 − coverage) per file, a node-level reduce |
per callable: CALLS sites arriving, in the test-reachable set (reach(tests, [CALLS]) ∪ TESTS); cfg(test) code is test code |
coverage_fn (line coverage) when empty; semver_diffs (P5); P6 |
churn |
live | band filter + hotspot rank | functions per file as the complexity | — |
bench_gaps |
live | hot-path rank, perf_hint, arm_covers_module, the gap |
per module: CALLS sites, files, test coverage | bench_runs / bench_results when empty; P6 |
undoc_api |
live | ranking by churn, authors | callables with effective visibility direct / reexported, has_doc; trait-impl methods and cfg(test) code are no API |
— |
weak_tests |
live | weak::weak_tests whole (churn keyed (repo, test)) |
outcome name → the ONE #[test] node it is a path suffix of → reach → files → churn |
unmatched names are counted; P6 |
reachable_vuln |
live | vuln_findings × sbom_components join, refuse / hold |
CALLS + USES_TYPE sites from the repo's own code into each crate@version |
direct use only (ring off); P6 |
generic_cost |
live | bloat::classify, the tax fold + report |
mono_instance nodes, INSTANCE_OF, symbol sizes per artifact (Room::symbols) |
a repo without a binary stream |
binary_drift |
live (node half) | is_glue / is_runtime / is_derive_impl / suspicious_tag, findings, report |
per linked symbol: first party + explained by the source (provenance both, or an instance of a source item) |
P4: no binary call edges — the edge half is kept and not evaluated; attestation reads SYMBOLS ONLY |
name_collision |
live | report shape; conflation = edges into a MERGED id, leaf names are the labelled baseline | Room::duplicate_keys over callables, CALLS edges out of own code |
P6 |
symbol_identity |
live | class split + verdict: repeated key in one unit / one id in several files (ours) vs units that disagree | Room::duplicate_keys over own definitions, spans |
— |
trust |
live | (max, min) semiring definition, local_trust, Bounds, ACT / HOLD / REFUSE, weakest hop |
Room::bottleneck from the entry surface over local-trust weights; weakest hop by Room::path through nodes at least as trusted |
the sensitive-access term is not applied (the edges exist since lane K; this lens does not read them yet); P6 |
world_impact |
live (workspace half) | rank = callers + constellation dependents, breaking first | Room::diff of a repo's last two consumer ordinals per crate; breaking = a removed public item; callers = Room::dependents grouped by repo; dep_graph_edges |
P5: no crates.io ring, no published semver diff; a repo needs two consumed commits |
subclone |
live | whole | item spans from the room's node spans (nodes_in_file); span-less files counted |
files the consumed configuration did not compile |
data_reach |
live (lane K, §10) | sensitivity_of, ranking |
Room::edges(TOUCHES_TABLE) from the request's code (the direct touch: access, sites), one backward Room::dependents_by([fn], [CALLS]) per toucher inside the scope (the reachers), effective_visibility (public entry); the runtime-named sites counted and listed under unresolved |
P6; a lower bound by what §10 names as not covered |
similar_code, semantic_clones |
not_wired |
scoring halves in mimir_clones.rs / mimir_analyze.rs |
— | the embeddings lane (item embeddings from driver spans) |
| data_reach | not_wired | sensitivity_of, ranking | — | P3 TOUCHES_TABLE has no producer | | similar_code, semantic_clones | live (lane J, §10) | analyze_duplicates / analyze_semantic_clones whole: the one predicate shares_one_chunk_vector, the trivial-family suppression, the precision filters | one vector per function node (vector::fnvec: node id → content hash → vector); signature from the PARAM nodes, callee set from CALLS, impl header from the block + IMPLEMENTS | no vector → vectors_missing / no_embedder (no count at all); N of M without one, or vectors of an older seq than the room → partial with the numbers; P1 (no return type in the signature); P6; the dependency ring's vectors are written but not compared |
The 6 mimir_calibrate tests deleted with the syn corpus are back (mimir_calibrate::tests::on_a_room_corpus): the corpus is lifted out of a code room (room_feed::corpus::from_room — item spans, parameter nodes, CALLS edges) on a generated tree; real_lake_calibration_corpus (ignored) runs the same oracle on a real lake. room_tests::real_lake_every_lens (ignored) asks every lens against a real lake.
6.5 Lane C — the other readers
| surface | kept half | room feed | missing |
Knowledge.SymbolLookup / DefinedIn (+ MCP knowledge_* via knowledge_query_json, CLI knowledge query) |
wire types, KnowledgeView row model (edda leaf) |
name lookup → node rows with file:line | R3, R1, R2, P1 (visibility) |
Knowledge.Callers / Callees |
wire types | callees reach(seed, [CALLS], Some(1)); callers dependents_by(seed, [CALLS]) one hop; site lines |
R3, R4, R2, R7 (count) |
Knowledge.CallPath |
wire type (NameList) |
a CALLS path | R6 |
| knowledge feature-gates query | — | local nodes with a cfg |
R1 (cfg); P2 (active configuration only) |
Graph-RAG knowledge context (knowledge::context::context_slices, takes the view as INPUT) |
the join, expand_call_radius, hydrate_source |
a bounded neighbourhood of the seeds (reach + dependents_by, N hops) with rows + spans |
R4, R1, R2 |
vector slices-index / slices-search |
index + search | item spans | R2 |
Introspect.Callers / Callees / PathBetween, Dwarf.Callers / Callees / CallPath (+ MCP callers_of / callees_of / path_between / dwarf_call*, CLI introspect …) |
wire types — ONE reader for both services | source CALLS restricted to linked nodes: within = mask(provenances=[both]); INSTANCE_OF maps an instance to its generic. Evidence: inlined / indirect calls absent |
R3 (symbol → node), R6, R4, P4 |
Viz.Knowledge (🗺) |
draw_bubble_chart, KnowledgeSummary / RepoScanSummary / Bubble |
Room::stats() per repo (nodes by kind, edges by kind) + per-crate node / call / file counts + 30-day heat |
R8, P7 |
Viz.Architecture board (🏛) + Ops.RunArch + CLI / MCP arch * |
board renderer, wire type | the module / crate quotient of CALLS ∪ USES_TYPE; history = Scope::repo(repo, Some(seq)) + Room::diff (replaces the historized architecture_wiring). Ops.RunArch has nothing left to generate (D4) |
R8 |
metro (🚇 tab + arch metro fuel, viz::metro_feed::build_metro_map, arch::metro) |
line / stop model, renderer | entrypoint → reach(entry, [CALLS]) in hop layers; termini = tables (live, lane K §10: ArchPayload.access = each gRPC handler's NEAREST table along CALLS + TOUCHES_TABLE); lit overlay = surface_coverage |
R6, R1 |
viz callgraph lens (viz::callgraph) |
renderer, provenance colours (test_inject_graph_with_provenance shape) |
edges of a crate / repo mask, capped; provenance from NodeView |
R7 |
viz release tab deep edges (viz::release_depdag) |
DAG renderer, deep_state |
repo × repo quotient of CALLS ∪ USES_TYPE on Scope::current() |
R8 |
Viz.TestMatrix inventory, CLI test list |
the ok / fail / X join, results half (live) | is_test nodes with crate / module / name / file:line |
live: is_heavy = the node's ignored (§9); a test consumed before mimir-schema 3 has none and is named as such |
autonom completeness gate (autonom::gather_with_utfallsrum) — FAILS CLOSED |
allowlist, utfallsrum threshold, GateState, persist → surface_coverage |
surface = public local fns; covered = TESTS targets ∪ reach(tests, [CALLS]) |
P1, R1, R5 — stays closed until P1 |
release doctor semantic blast (release::doctor::run_with_warehouse_semantic_blast) — FAILS CLOSED |
link gate, audit record, fixers | delta of the DIRTY tree vs the open set; then Room::dependents(changed) (the Jikes walk exists) on Scope::current() |
WIRED by lane E (§9): code_room::reader::pending_blast; still fails closed, typed (release::doctor::BlastRefused), when the blast cannot be derived |
funnel decompose / funnel prompt grounding (funnel::decompose::decompose(task, retrievers, …)) |
decomposition, judge loop | a Retriever over the room: name match → anchors with file |
R3, R2 |
Mimir.RepoOverview knowledge block (mimir::repo_overview) |
deps / dependents / build order | Room::stats() for Scope::repo(repo, None) + offset sha / seq + evidence.missing |
none in the room (R13 / R14 only) — first lane C surface that can go live |
Mimir SQL templates (MCP mimir_sql_template) |
— | def-use = one-hop dependents_by over all kinds; change-radius = dependents; call-chain = path; table lineage = TOUCHES_TABLE (live, lane K §10) |
R4, R6 |
coderoom snapshot-diff / what-breaks (CLI) |
— | Room::diff (exists) / diff + dependents |
P1 for "breaks"; ingest / compat = P5 |
6.6 Three lanes, disjoint files
Proposed first step, done by lane A ALONE before B and C branch — the seam commit. It adds no behaviour:
- root
Cargo.toml: optional depsmimir-ingest,nornir-mimir-roombehindcode-room(§5 point 4);src/lib.rs:pub mod code_room;. src/code_room/mod.rs: the per-workspace handle (lake path, shared log handle,room(scope)), built inopen_workspace_ctx, held as a newWorkspaceCtxfield (WorkspaceCtx.mimiris already the dep graph — pick another name).- Two dispatch points that today return exactly what the stubs return:
mimir_lens::answer(lens, handle, params) -> Value(file owned by B) andcode_room::reader::*(file owned by C). Everynot_wiredsite inserver_grpc.rs,mcp_stdio.rsandbin/nornir.rsis re-pointed at them ONCE.
After the seam, B and C edit only their own modules; the three shared files are touched again only by A.
| lane A — server wiring | lane B — lenses | lane C — other readers | |
| owns (new) | src/code_room/{mod,consumer_loop,status}.rs |
src/mimir_lens/room_feed.rs (room → lens inputs, Evidence → feeds / evidence_note), body of mimir_lens::answer |
src/code_room/reader.rs |
| owns (existing) | src/monitor.rs, src/registry.rs, src/warehouse/populate_completeness.rs, src/warehouse/workspace_events.rs, src/warehouse/clone_events.rs, root Cargo.toml, src/lib.rs, .woodpecker.yml, .nornir/running-as-a-server.md |
src/mimir_lens/*.rs, src/mimir_analyze.rs, src/mimir_calibrate.rs, src/mimir_clones.rs, src/mimir_subclone.rs |
src/knowledge/{query,context,mod}.rs, src/mimir.rs, src/autonom/mod.rs, src/release/{doctor,pipeline}.rs, src/funnel/decompose.rs, src/introspect/*, crates/nornir-introspect, src/arch/*, src/viz/{knowledge,arch_tab,metro_tab,metro_feed,callgraph,release_depdag,release_tab,remote}.rs, src/warehouse/sql.rs, src/vector/* |
| crates | crates/mimir-ingest (I1–I6) |
crates/mimir-graph (R11) |
— |
crates/mimir-room |
R13, R14, R15 | R9, R10, R11, R12 | R1–R8 |
Unavoidable overlaps and the split:
| file | A | B | C |
src/server_grpc.rs (6 524 lines) |
WorkspaceCtx (136–175), WorkspacesSvc (4419–4942: register 4524, fetch 4777, repopulate 4910), OpsSvc::scip_ingest (5344), open_workspace_ctx (5379), fetch_served (5492), run_with_policy (5644–), tests mod (6434–) |
MimirSvc lens handlers only (2836–3090) |
IntrospectSvc (2321–2341), KnowledgeSvc (2346–2400), DwarfSvc (3428–3447), VizSvc::test_matrix 3927 / knowledge 3980 / architecture 4031, OpsSvc::run_arch 5330. (MimirSvc::repo_overview 2807 calls mimir::repo_overview — C edits src/mimir.rs, not the handler) |
src/mcp_stdio.rs (5 580) |
workspace tools (workspace_register 1209 and the fetch / populate tools after it) |
mimir_* tools 1690–2226 |
knowledge_context 2379, mimir_sql_template 2630, callers_of / callees_of / path_between 3688–3731, arch_graph_for 4128, dwarf_* 4285–4328, knowledge_query_json 4722 |
src/bin/nornir.rs (16 763) |
run_workspace 8305–8550 |
run_mimir_analyze 9550–9600 |
run_funnel_prompt 4400, run_funnel_decompose 4774, run_map 5831, run_arch 6215, run_knowledge 6241, run_knowledge_context 6339, run_coderoom 9440, run_test_list 12640, run_introspect 15204, run_vector 15852 |
crates/mimir-room/src/query.rs, lib.rs, snapshot.rs |
handle model (R13–R15) | — | node attributes, spans, lookup, path, edges, quotient (R1–R8) |
Each lane's hunks in the three big files sit in different impl blocks / functions, hundreds of lines apart, so they merge textually even without the seam; the seam makes them not overlap at all. The real contention is crates/mimir-room: B needs R1 / R2 / R4 / R5 / R8 that C owns. Order: A's seam + R13 first; then C lands R1–R8 as its first commits (API only, with tests) and B rebases on them; B's room work (R9–R12) is in files C does not open (ops.rs in mimir-graph, a new symbols.rs in mimir-room).
What can go live without any missing API, as the proof each lane starts with: A — consume one member + Repopulate report; B — anomaly (needs only P7's git-heat trigger); C — Mimir.RepoOverview knowledge block (Room::stats()).
7. Decisions needed
| id | decision | suggestion |
| D1 | lake location: own skade catalog under builds/mimir_v2/lake vs tables inside builds/catalog.redb |
own catalog |
| D2 | first offset of a newly registered member: root commit (full history, hours for a big repo) vs tip | tip (pass tip^ as initial_offset); backfill on request via Repopulate |
| D3 | release doctor on a DIRTY tree: dry-run delta in ingest (I3) vs "the bump must be a commit on a branch, diff two seqs" | dry-run delta — the gate runs before the commit exists |
| D4 | Ops.ScipIngest and Ops.RunArch: delete the verbs (edda nornir-wire) vs re-purpose |
delete; they name deleted roads |
| D5 | GraphBLAS: system library + package in images vs vendored static (§5) | system library |
| D6 | driver delivery to a server box: mimir-driver is a separate nightly artifact (two binaries + the pinned toolchain with rustc-dev) |
an ops-kit step; the server answers "driver not installed" by name until then (I5) |
| D7 | publish = false on the mimir crates until the branch lands (§5 stage row) |
yes |
8. Lane A — what landed (mimir-v2-wire-a, 2026-10-13)
| step | what | where |
| seam | code-room feature; CodeRoom on WorkspaceCtx; Reach, Refusal, State; the two dispatch points mimir_lens::answer(lens, reach, params) (B) and code_room::reader::{answer, knowledge_view, arch_graph} (C); every front door re-pointed once; guard tests/code_room_seam.rs |
src/code_room/{mod,reader}.rs, src/mimir_lens/mod.rs, the three shared files |
| R13 | EventLog = a shareable handle: many readers (pin() = ONE committed state of the lake, never waits for the writer), one writer (LogWriter seat, held by the Consumer), ONE process (LakeHeld), and the lake's OWN runtime (an Iceberg table handle captures the runtime it was built on; the catalog caches handles) |
crates/mimir-ingest/src/log.rs |
| R14 | Room::at_blocking / open_blocking, EventLog::block_on, CodeRoom::room(scope) |
crates/mimir-room/src/lib.rs, src/code_room/mod.rs |
| R15 | ScopeError::{NotConsumed, PastOffset, NegativeSeq} → State::NotConsumed / InvalidArgument |
crates/mimir-room/src/scope.rs |
| I1 | DumperPool<D> (a dumper per repo behind one consumer), impl Dumper for Box<D> |
crates/mimir-ingest/src/dump.rs |
| I2 | Consumer::consume_run(repo, Run): budget, cancel between commits, on_begin / on_commit, replay_from |
crates/mimir-ingest/src/consumer.rs |
| I3 | the dry-run delta (decision D3): DriverDumper::dump_tree(tree) (a FULL dump of every local unit of an uncommitted tree, a dumper of its own, the tree only read) + dry_delta(log, open, repo, dump) → DryDelta { nodes / edges / spans: added · changed · removed, failed_units } (Consumer::dry_delta, EventLog::dry_delta for a reader with no consumer); nornir CodeRoom::dry_run(driver, member, tree) / dry_delta_of. Nothing is written. The release doctor (src/release/doctor.rs, lane C) is NOT wired to it yet: reach.room.dry_run(…) → DryDelta::touched_nodes() → Room::dependents |
crates/mimir-ingest/src/{consumer,driver,log}.rs, src/code_room/consumer_loop.rs |
| I4 | first offset = TIP (git::tip + offset_before); `--from <sha |
date> (git::resolve_from`); the first commit of a stream that does not start at the root is dumped even when docs-only |
| I5 | DriverHost::{Installed, Wrapped} (the invocation seam), DriverSpec::probe → DriverUnavailable::*; nornir side code_room::driver::Settings → State::DriverNotAvailable |
crates/mimir-ingest/src/driver.rs, src/code_room/driver.rs |
| I6 | status::drop_v1_tables (the nine syn-era tables, lens_*, the chain's ledgers) behind Repopulate{purge} |
src/code_room/status.rs |
| consumer | CodeRoom::consume / status / behind / room_line; server spawn_consumer_loop, server_sweep, consume_served (the ONE door), Register / Fetch / Repopulate / Remove; CLI fat mode |
src/code_room/{consumer_loop,status}.rs, src/server_grpc.rs, src/bin/nornir.rs |
| P7 | the feeds ride the offsets: git_heat, sbom, dep_graph, code_index, embeddings; ledger mimir_v2.consumer_feed_runs |
src/code_room/feeds.rs |
| D4 | CLI arch generate, viz::remote::run_arch deleted; the two Ops trait methods answer the deleted-verb status until the proto drops the rpcs |
src/bin/nornir.rs, src/viz/remote.rs, src/server_grpc.rs |
| D5 | code_room::preflight in release::pipeline::run_cargo_test; .woodpecker.yml |
src/code_room/preflight.rs |
| D7 | publish = false on the five mimir crates that lacked it |
crates/*/Cargo.toml |
Decisions as implemented: D1 the lake is <ws>/builds/mimir_v2/lake, its own catalog. D2 TIP, backfill by nornir workspace populate <ws> --from <sha|date> (inline; the wire has no field). D6 the driver is located by settings and probed; NORNIR_MIMIR_DRIVER_HOST is the wrapper line of the toolchain image. A lake of an OLDER mimir-schema that only lacks nullable columns is evolved in place on open (lane E, §9); any other difference is refused (LakeSchemaMismatch), and the message names the directory to remove.
Follow-ups outside this repo (edda)
| where | what |
nornir-wire proto |
CLOSED by lane F (edda mimir-v2-consumers-2026-10-13): rpc RunArch / rpc ScipIngest + RunArchRequest / ScipIngestRequest are out of the proto, the two tombstone methods are out of server_grpc.rs (no caller in any sibling) |
nornir-wire proto |
a from / member field on WorkspaceFetchRequest (or WorkspaceRepopulateRequest) so a backfill can be asked over the wire; a typed status RPC instead of strings in WorkspaceRepopulateReport |
nornir-build-thing buildgate.rs:961–963 |
GraphBLAS preflight before cargo test --workspace --no-run --tests / cargo build --workspace --bins |
nornir-testmatrix targetcheck.rs, testsurface.rs:748 |
the same preflight before cargo build --workspace --tests --keep-going / cargo test --workspace |
nornir-release-core edition.rs:403 |
cargo doc --workspace --all-features switches on graphblas-sys/regenerate (bindgen, needs libclang-dev): take regenerate out of cargo features (land map §5.5) |
toolchain image / cargo-test.Containerfile |
libgraphblas-dev; the driver pair + the pinned nightly with rustc-dev (D6) |
| runtime images / ops kits (gunnar and holger) | libgraphblas7 + libgomp1 wherever nornir-server / dwarves-full ship |
nornir-git gitio::fetch_repo |
CLOSED by lane F (same edda branch): (1) the re-fetch opens through open_for_ref_edits (the committer fallback); (2) a non-SSH re-fetch follows the remote's HEAD — branch, HEAD and working tree — like ssh_sync, so clone_or_fetch returns the new sha and a local-path / https member shows as behind. The consumer's own clone still does not go through it (mimir_ingest::git::sync_clone: there HEAD and the tree are the checkout step's) |
nornir-warehouse-trait |
CLOSED by lane F: Warehouse::append_symbol_scan_async and Warehouse::load_latest_scip are gone from the trait (the Iceberg and remote impls with them) |
nornir-release-core |
CLOSED by lane F: semantic_blast::compute no longer reads scip_occurrences / symbol_facts / call_edges; a bump with dependents fails with the typed CallGraphNotProduced. rank(view, …) is the kept half for a caller that holds a call graph; nornir's own doctor (lane E) builds its SemanticBlast from the room's dependents walk and calls neither |
jera::jobs::kind |
kinds of its own for a consumed commit and a feed run (today: knowledge_member, and knowledge_scan / security_member / index_build / vector_embed) |
Known limits
- The feeds are keyed on the consumer's offsets: a box without the driver runs no feed either (git heat included), and says
driver_not_available. - A consume and its feeds hold the workspace gate;
embeddingson a CPU embedder is minutes per member (measured in a DEBUG build: 30 chunks, 415 s). A fetch waits for it, readers do not. ingest_runs.msdoes not include the commit phase (the row is built before the release); the job row'sphase_ms.commitdoes.- The toolchain's std dump (
StdDumper) and the binary layer are not run by the server (source stream only). - The root crate cannot be published from this branch: it depends on the unpublished mimir crates (D7), by design until landing.
9. Lane E — closing items (mimir-v2-wire-e, 2026-10-13)
| item | what | where |
| schema evolution | a lake at an OLDER mimir-schema is evolved in place when it is opened: per table ONE metadata commit (an Iceberg transaction through skade's catalog: update_schema().add_column(optional) + the table properties mimir.schema = the version, mimir.added = the columns added in place). No data file is touched, no wipe. Evolved = the table only LACKS nullable payload columns outside the key (a bookkeeping table: any nullable column). Refused, typed (LakeSchemaMismatch { why: Unevolvable::… }): a column this build does not have, a retyped column, a missing key / non-nullable column, another sort order, a lake stamped NEWER than the build |
crates/mimir-ingest/src/{log,schema,delta}.rs |
| … the hash | the payload hash covers a column added in place only where the row HAS a value (PayloadSpec::added, read from mimir.added): a row that is null there hashes as it did before the column existed, so the next consume writes an event only for a row the producer now says something about. A fresh lake hashes every column as before (no change for lakes that never evolved) |
delta::row_hashes |
| … the readers | Iceberg scans at the SNAPSHOT's schema and an evolution takes no snapshot: until the first release after it, a scan is asked only for the columns the snapshot has and the others come back as nulls (scannable / null_filled); writes go in the table's column order (in_table_order: skade writes by position, added columns are last) |
log.rs |
| … the dumps | the dumper's mirror is stamped log identity + schema (EventLog::dump_identity): the first consume after an evolution discards the kept dumps and dumps every unit again, once — so the new columns get their values at the member's next commit, for every crate, not only the ones the commit touched |
log.rs, consumer.rs |
| P1 attributes | mimir-schema 3: code_nodes.ignored, code_nodes.doc_hidden (nullable booleans, appended); the driver reads them from rustc (AttributeKind::Ignore, tcx.is_doc_hidden); Viz.TestMatrix / Test.List is_heavy = ignored (a test with no value is counted and named, never false); undoc_api leaves out #[doc(hidden)] functions (own attribute or a hidden module above) |
crates/mimir-schema, crates/mimir-driver/src/walk.rs, src/code_room/reader/wired.rs, src/mimir_lens/room_feed/{graph,lenses}.rs |
| doctor blast (D3) | release::doctor::run_with_warehouse_semantic_blast(repos, policy, reach, driver): per dirty repo code_room::reader::pending_blast — dry-run dump of the dirty tree → delta against the consumed state → the member's symbols the tree changes / removes (a param lifts to its fn, a field also to its struct; a version bump alone removes nothing: same path under the new version = same item) → the sites in OTHER repos that name them (one hop back over CALLS / USES_TYPE / IMPLEMENTS / FOR_TYPE / READS_FIELD) + the transitive Jikes count → SemanticBlast (source = "code_room"), the input of gate::semantic_link_gate. Fails closed, typed (BlastRefused): no room, lake held, the repo or a repo that depends on it not consumed, driver not available, the dirty tree does not compile. A dirty tree that changes no node = an empty blast. The one in-tree door: MCP release_doctor with semantic_blast: true |
src/code_room/reader/{blast.rs,../reader.rs}, src/release/doctor.rs, src/mcp_stdio.rs |
| heat | Viz.Knowledge heat_30d = commits of the last 30 days on the crate's hottest file: each repo's newest git_heat_facts scan joined on the files the crate's items lie in. git_heat_facts is named missing only when no scan was read (whole table, or per repo) |
src/code_room/reader/wired.rs |
| target ids | two targets of one package never share an id: a non-lib target's nodes start with <kind>=<crate_name>: (moniker::target_prefix: bin=lgz: , test=x: , bench=x: , example=x: ); which target a unit is comes from cargo metadata (the target whose root file is rustc's input), not from the crate name. Before: <crate_name>: for every non-lib target, and "lib-like" decided by name — a bin named like its lib had the lib's ids in its --test harness, and a test and a bench of one name shared all of theirs. The lib's ids are unchanged (rust-analyzer's, the byte-identity test) |
crates/mimir-schema/src/moniker.rs, crates/mimir-driver/src/{lib,walk}.rs, crates/mimir-binary/src/crates.rs |
| branch | the branch a member follows is named — workspace descriptor, else the clone's own branch, else its remote's default (refs/remotes/origin/HEAD) — never guessed; with none, or a named branch the clone lacks (git::NoSuchBranch), the member's turn ends no_branch with what to set |
crates/mimir-ingest/src/git.rs, src/code_room/consumer_loop.rs |
What an existing lake sees
- A schema-2 lake under this build: opens;
code_node_eventsandbin_node_eventsgainignored,doc_hidden; every event table is stampedmimir.schema = 3. Nothing is written to the event tables until the member's next commit. That consume dumps every unit (mirror discarded) and writes+events for the nodes the producer now has a value for — every defined item (doc_hiddenisfalseortruewhereverhas_docis set) — and nothing for edges or spans. Measured with the real driver on the driver fixture, a lake written by the schema-2 build (308 node, 408 edge, 317 span events of the repo): opened by this build →code_node_events24 → 26 columns, the open-set cache still valid, 0 events; the next commit (one new fn in ONE crate) → all 7 units dumped, 112 node+events (110 identities that existed and now carry a value, 2 for the new fn under its two owners), 2 edge events, 4 / 2 span events. 198 of the 308 open node identities — stubs, params, generic params, features — were not written again. - The target ids: at the member's next consume every node of a non-lib target (bin, integration test, bench, example) closes under its old id and opens under the new one, with its edges and spans. Measured on the same lake: a comment-only commit in the crate that has the bin
bctl→ its 2 nodes under their 2 owners (the bin and its harness) move: 4-/ 4+node events, 21 / 21 edge events, 16 / 16 span events. No migration. - The dependency ring (
@universe) is dumped once per crate version: its rows keep null in the new columns.
Known limits (lane E)
- skade's all-core raw decode declines a table that has more than one schema (
skade/src/read.rs:metadata.schemas_iter().len() > 1): an evolved lake is read through the Iceberg engine's scan. Same rows, slower. The fix is in skade (a field-id remap in the raw path). - Under a version bump the doctor's blast is exact for REMOVED symbols; a change of a surviving item's type alone is not told apart (every id of the crate moves with its version, and the dry-run delta carries keys, not columns).
doc_hiddenis the item's own attribute;undoc_apiwalks up to find a hidden module, other readers must do the same.- The semantic blast is a lower bound like every reachability answer (P6), and it reads the sites of CONSUMED repos only — which is why a dependent that is not consumed refuses instead of answering.
10. Lane H — the wire door and the panes' readers (mimir-v2-wire-h, 2026-10-13)
A thin client (dwarves) had no way to ask the reader: only five Knowledge.* verbs and three Viz.* verbs were on the wire, typed or as JSON, and a refusal was a gRPC status (the state lost in a code). Lane H adds ONE door and the surfaces the dwarves panes draw.
| item | what | where |
| wire door | CodeRoom.Answer(surface, params_json) → JsonResponse (edda nornir-wire, branch mimir-v2-wire-h): any surface of reader::SURFACES by name. The body ALWAYS carries state and surface; a refusal is a body (refused: true, the state, its sentence in note), never a gRPC error; no served workspace selected answers no_room. Auth and transport failures stay gRPC errors |
reader::door / refusal_body, server_grpc.rs CodeRoomSvc |
| room identity | every pane surface carries room: workspace, repo@seq with the commit, node count, edge counts by kind — the numbers a pane prints so they can be compared with the server |
reader/wired/panes.rs::identity |
Viz.CrateGraph |
the quotient by crate: one row per name@version (local or not, nodes, symbols, fns, files), per pair depends_on / calls + call_sites / uses_type; per repo its crates and what they depend on; repo → repo. universe: true folds the ring |
panes::crate_graph |
Viz.CallGraph |
the call-graph lens (call_graph) through the dispatch, with the room |
panes::call_graph_answer |
Viz.FunctionTier |
per repo, per local crate: its fns / methods and the CALLS edges among them (indexes into the crate's fns, with sites) | panes::function_tier |
Knowledge.Symbols |
a capped listing of symbol rows (SymbolLookup is a search), with end_line, path, moniker |
panes::symbols |
Knowledge.Source |
one item, named by moniker or name: its span, and the text of the span read from the blob at the consumer offset's commit in the consumer's own clone (<builds>/mimir_v2/clones/<member>). The caller names a node, never a file |
panes::source |
Knowledge.TestReach |
every local fn / method with test_reachable (reach(tests, CALLS) ∪ TESTS targets), test_code, fan_in; every test with what it calls, names and reaches and the files those lie in |
panes::test_reach |
Coderoom.SnapshotDiff |
also by repo alone (its last two consumer ordinals; with one consumed commit, the empty state before it — said in a note); by_crate (symbols / nodes added, removed, changed per name@version, crates in only one state); room_from / room_to |
wired::snapshot_diff, panes::diff_by_crate / last_two |
Unchanged on purpose: Coderoom.Ingest / Compat / WhatBreaks (P5), table lineage (P3), the vector lenses, binary call edges (P4) — not_wired through the door as through every other front door.
10. Lane K — which code touches which table (mimir-v2-wire-k, 2026-10-13)
P3 closes: TOUCHES_TABLE has ONE producer, the rustc driver (crates/mimir-driver/src/walk/tables.rs — its header is the definition of "touching a table").
| item | what | where |
| the anchor | the storage engine's own typed API, crate iceberg: TableIdent::new / .name (a name), Catalog::load_table (name → table), Table::scan / reader_builder (READ), Transaction::new (WRITE), Catalog::create_table / drop_table / … (WRITE). No list of nornir, skade or edda fn names anywhere |
tables.rs engine_api, HANDLE_TYPES |
| derived, not listed | every fn between a caller and the engine gets a summary: which parameter names a table it reads / writes, which table its return value stands for. A fn that only passes its parameter on is a conduit (no edge); the edge is written where the name stops being a parameter — a literal / const / static (const-evaluated, through re-exports, also a const list of names) → that table; a runtime value → the <unresolved> table node, counted |
tables.rs |
| across crates | each compiled crate writes its summaries beside its sidecar (<key>.tables.json, keyed by DefPathHash, versioned TABLE_PASS); a call through a trait takes the union of the summaries of its impls (this crate's and its dependencies') |
mimir-driver lib.rs TableSidecar |
| the one annotation | a trait whose impls are DOWNSTREAM of its callers has no body to derive from: #[cfg_attr(mimir, mimir::table(read | write | both, <param>))] on the method. cfg(mimir) and the tool exist under the driver only (the clippy / kani shape). Not needed anywhere in nornir |
tables.rs, main.rs |
| schema 4 | additive: code_edges.access (nullable: read / write / both, set on TOUCHES_TABLE only), node kind table, the table id moniker::table(name) = rust-analyzer cargo @warehouse . \table=`:` — the NAME and nothing else, so two crates of two repos meet at one node. A schema-3 lake gains the column in place |
crates/mimir-schema |
| the room | EdgeOut.access, Room::access_of (the union over the units that carry an edge), Room::edge_column_added_in_place (an older lake: no edge is not yet a fact) |
crates/mimir-room |
data_reach |
per (table, fn that touches it): access, sites, the fns that reach it along CALLS inside the request's repos, public entry; per table: readers / writers / reach; unresolved: the runtime-named sites by fn and file:line |
src/mimir_lens/data_reach.rs, room_feed/lenses.rs |
| table lineage | Mimir.SqlTemplate table-access-lineage {table[, repo]}: one row per site of the direct touchers + reached_from; a name no fn touches is invalid_argument and names the tables there are; <unresolved> lists the runtime-named sites |
src/code_room/reader/wired.rs |
| the board and the metro | the board's Table nodes and reads / writes edges are the room's edges; ArchPayload.access = for every gRPC handler the table(s) nearest to it along CALLS + TOUCHES_TABLE (Room::min_hops_to + one bounded reach), which the 🚇 pane hands to metro_feed::build_metro_map. A handler impl is recognised under the trait's resolved name too (impl Bench for BenchSvc): before that no handler of a real room was a gRPC node |
wired.rs, src/viz/arch_tab.rs, src/arch/mod.rs |
| gone | the closed map of accessor NAMES (accessor_table_access) that the v1 scan classified call sites with |
src/warehouse/access_scan.rs (the row shape stays) |
Measured on real code (the nornir lib, default features)
The real driver (release build) over -p nornir --lib, tree = 46f10dd + this lane's work as it stood before the last lens / reader edits (the warehouse sources are those of the head):
- 191 TOUCHES_TABLE edges, 213 sites, 67 tables by name; 105 write, 79 read, 7 both; 5 sites in 5 fns name their table at runtime (the gRPC
Warehouse.Scan/ScanVectorshandlers, the warehouse browser pane,compact_all,drop_v1_tables— each checked in the source: runtime names all five). - The pass looked at 154 bodies (227 looks) of the lib's 7 049 fns and methods, kept 44 summaries, read 1 185 dependency sidecars: 0.16 s of a 1.72 s walk.
- Fed through the consumer into a scratch lake (the driver's Arrow files, one commit): 165 567 node + edge rows and 126 341 span rows in 3.8 s. The room opens in 4.9 s cold (36 898 nodes);
data_reachanswers in 0.2 s (186 (table, fn) rows, 67 tables, the 5 runtime-named sites listed); the lineage ofbench_runs= 6 sites, 255 fns reach them; the board has 67 Table nodes and 153 reads / writes edges; 87 gRPC handlers get a nearest table (114 pairs, 1.3 s for the whole board). - NOT run: the consumer with the real
DriverDumperover the whole workspace and the dependency ring (started, stopped after a minute when the owner stopped driver consumes on dwarf, 2026-10-14 01:25 CEST).
Against the v1 scan (nornir main's access_scan.rs, unchanged, run over the tree of 46f10dd: 441 site rows, 22 tables by name + <dynamic>): v1 marks the CALLER of a named accessor, this marks the fn where the name meets the read / write, so the same fact sits one CALLS hop lower and the reachers hold v1's fn. v1 never saw 48 of the 67 tables (its closed list of accessor names did not have them, and it read string literals only — scan_preview(TABLE, …) was <dynamic>); it counted table_names() as an access (most of its 20 <dynamic> rows); about two thirds of its rows (290, counted by path and module name) are #[cfg(test)] modules, tests/ and examples/, which here belong to the harness / test / example units — not in this lib-only run. v1 is right where this is blind: its 5 release_events rows in the lib are calls into edda's release ledger (below).
Not covered (the readers say so; none of it is silently empty)
- Calls through
&dyn Warehousemade inside edda (nornir-release-ledger:release_events,release_changes;nornir-knowledge-view): those crates are compiled before any impl of the trait exists, so there is nothing to derive. The fix is ten lines inedda/crates/nornir-warehouse-trait(#[cfg_attr(mimir, mimir::table(write, table))]onappend_arrow/append_arrow_async/ensure_table/ensure_columns,readon thescan_*/describe_columnsmethods, andcfg(mimir)declared in its[lints.rust]). korp-collectors'WarehouseSinkis the same case. Inside nornir a&dyn Warehousecall resolves through nornir's own impl. - SQL: a query string is not read for table names, and a table DataFusion scans through a registered provider is no edge.
- A table reached through a closure parameter or a fn pointer (P6's shape).
- The namespace / catalog: the id is the table's name. The warehouse, the Mímir v2 lake and modgunn's own store share one name space of ids.
- A loop over a list of name PAIRS attributes each site to every name of the list (the right tables, each site counted for all of them).
- Creating a table (
ensure_layout, create-if-missing) counts as a WRITE of it: on nornir that one fn writes 66 tables and, being reached by everything that opens a warehouse, headsdata_reach's ranking. 24 of the 67 tables are touched by it alone. - A handler's nearest table is nearest in the CALL GRAPH: a handler that goes through a dispatcher fn ends at whatever the dispatcher can reach.
- After a change to what a summary says (
TABLE_PASS), dependencies cargo keeps are not summarised until they are compiled again: every dump names them (table_sidecars_stale). For the schema-3 → 4 step itself the dumper discards its mirror and cleans + compiles again every unit that has no dump (read frommimir-ingestdriver.rs, not run on an existing lake here).
10. Lane I — the server runs all of it (mimir-v2-wire-i, 2026-10-13)
| item | what | where |
| backfill | workspace populate <ws> --from <sha|date> [--member m] writes ONE requested row per member into the lake (backfill_runs) and returns; the walk is a background job of the server, one commit per hold of the workspace's gate (WorkspaceGate::give_way before each turn, so a waiting question or sweep goes first). While it is open the member is Backfilling in every other run. A commit pushed meanwhile is the last commit of the same walk. After a restart the sweep reads the open rows and goes on after the offset (resume_backfills). The request rides Telemetry.SubmitJobs (a queued workspace_populate record whose detail is {"backfill":{"from","member"}}): the wire has no field for it |
src/code_room/backfill.rs, src/server_grpc.rs (run_backfill, request_backfill_served), crates/mimir-ingest/src/{log,schema}.rs |
| binary hook | feed binary: after a commit's release the server looks in <builds>/mimir_v2/artifacts/<member>/<sha>/<profile>/<bin> (a cargo target dir of that commit, deps/ optional) and joins what is there against the lake's own source rows. It never builds. Nothing there = ledger status no_artifact with the directory it looked in; the feed is tried again when the directory appears. The binary lenses say no_artifact / "artifacts are of an EARLIER commit" instead of an empty answer |
src/code_room/feeds.rs, crates/mimir-ingest/src/artifact.rs, crates/mimir-binary/src/lib.rs (run_with) |
| std in the ring | the first consume of a lake dumps the toolchain's std once (std@<toolchain> job) before the members. Knowledge.CallPath falls back to a room with the ring when the repo's own room has no path. mimir-schema 4: code_nodes.link_name (the symbol an extern item links to) |
src/code_room/consumer_loop.rs (ensure_std), src/code_room/reader/wired.rs, crates/mimir-schema, crates/mimir-driver/src/walk.rs |
| driver image | containers/mimir-driver/Containerfile: debian:trixie by digest, the pinned nightly with rustc-dev + rust-src, gcc, git, pkg-config, the two driver binaries built in the image; it asserts its own contents. Run through NORNIR_MIMIR_DRIVER_HOST |
containers/mimir-driver/ |
| embeddings | a feed can be DEFERRED (Feed::prepare): the consume only reads the sources; the embed runs on the workspace's feeds lane with no gate and no consumer held |
src/code_room/feeds.rs, src/monitor.rs, src/server_grpc.rs |
| a failure outside the commit | a dump that fails for a reason that is not in the commit's tree (a sibling clone missing, the toolchain, the disk) does not move the offset. After 3 failures of the same kind the member is blocked until it moves, repopulate / fetch --force, or a restart. A commit that is not ok says why on its line and in status |
crates/mimir-ingest/src/driver.rs (load_failure), src/code_room/consumer_loop.rs |
| siblings | before any member is dumped every member's clone is fetched; while a member is dumped every sibling's clone is at the tip of its followed branch, not at its offset | src/code_room/consumer_loop.rs (siblings_at_tip) |
| re-register | Workspaces.Register on a served workspace keeps what the server knew of the members that stay, says what was added / removed, and clones an added member at once (outside the work gate). A fetch merges what it learned into the row as it is now (Registry::merge_member_states) — before, it wrote back the row it had read and undid a register that landed meanwhile |
src/registry.rs, src/monitor.rs, src/server_grpc.rs |
| a member and its published copy | the driver decides "member" by the unit's manifest dir, not by name and version (VersionMap::is_member); in the lake the member keeps its plain owner id, the published copy is …#dep. This is what stopped edda (nornir-cli-outcome 0.1.4 is a member and also comes from the registry) |
crates/mimir-driver/src/{lib,walk,main}.rs, crates/mimir-ingest/src/driver.rs |
Measured (one box, load 25–35 of 12 cores, release build of 82827fc)
Through the server door, workspace of one member (znippy-zoomies, 608 crates):
- std: 25 crates, 937,960 rows, 51 s (90 s on a second server at the same time).
- tip: +3,242,285 events in 299 s (dump 156 s, delta 121 s, commit 22 s).
- binary feed with no artifact:
no_artifactand the directory, 8 ms. A hand build (cargo build --release -p lgz --target-dir …/<sha>) moved into place: picked up by the next sweep, 36 s, +3,353 rows. - call path
thp_enable_for_process→open64: 9 hops through std to libc'sexternitem; 110 s the first time (the room with the ring is built), 3.8 s after. →prctl(libc, one hop): 0.2 s. - backfill from 20 commits back: the request returned in 0.6 s; 21 turns (20 + one commit pushed during the walk, consumed last) in 173 s. 18 of the past commits do not load against TODAY's sibling (
nornir-build-thing = "^0.1.12", two with merge markers inCargo.lock): each is recordederrorwith that reason in 0.5–4.7 s and the walk goes on. - embeddings (tract CPU, 100 chunks, budget off): a commit pushed 20–28 s into the embed began its consume 848.0 s after the push before, 1.3 s after;
Workspaces.Fetchreturned after 864.0 s before, 17.5 s after. - memory of the server: 57 MB idle; 4.4 GB at the peak of the tip consume (the delta step), 0.33 GB 40 s later; 4.6 GB while the room with the ring was built and the embedder ran; 0.7 GB during the backfill walk.
Known limits (lane I)
- A past commit is checked against the siblings as they are today. When it does not load it is recorded
error; its rows are not rebuilt. - The artifact directory is filled by whoever builds; a descriptor key for another place needs a field on edda's
RepoSpec. - A member removed by a re-register is said; its rows stay in the lake.
- A sibling that is not a member of the workspace must be beside the clones by hand (
git/andbuilds/mimir_v2/clones/). - The sweep can queue one more backfill unit just as the walk ends: it finds nothing open and leaves a
donejob row that says so. - Not run: the image against the installed driver (same ids, cold / warm), a restart in the middle of a walk on a real server,
eddaitself, the real-driver tests added here (real::a_member_and_the_published_copy_…,real::a_member_that_is_a_path_dependency_…).
10. Lane J — the two vector lenses (mimir-v2-wire-j, 2026-10-13)
similar_code and semantic_clones answer from the code room again, on one vector per function. Design note: .nornir/vector.md § "One vector per function node".
| item | what | where |
| chunk | one chunk = the text of one function-like node: kind fn (rustc DefKind::Fn, not foreign) and method (DefKind::AssocFn: methods, associated fns, a trait's own methods), cut by the driver's span of that node at that commit, common indentation removed. Not chunks, counted: a function a macro wrote (EXPANDS on the item or on its impl / trait block — it is positioned at the invocation), a function in build-script output (absolute path, outside the checkout), a node without a span, ffi, closures. A text over 120 lines / 8 000 bytes is embedded in pieces and pooled into ONE vector |
src/vector/fnvec.rs, fnvec/room.rs |
| key | node id (SCIP moniker) → content_hash (SHA-256 of the text) → vector. The vector is a row of embeddings; identical bodies share it |
fnvec.rs |
| link store | embedding_node_events (repo · seq · sha · op · moniker · content_hash · chunker · ts), append-only, in the WAREHOUSE catalog beside embeddings; state at ordinal N = fold of seq ≤ N. embedding_node_runs = the ledger (one row per member run and per ring crate: model space, backend, counts, status, reason). embeddings_ring = the ring's vectors, the schema of embeddings, a table of its own (every reader of embeddings scans the model's rows) |
fnvec/links.rs, warehouse/iceberg_schema.rs, iceberg.rs |
| feed | the embeddings feed runs two halves: FUNCTIONS (fnvec::room::embed_member: cut every function node at the consumed commit, hash, events only for the nodes whose text changed, embed only content without a vector; the model is loaded only when a text needs one), then the LINE WINDOWS as before (free-text search). FeedAt carries the event log; Sink.jobs the job ledger |
code_room/feeds.rs, consumer_loop.rs |
| ring | not in a consume. When the ring's manifest snapshot moved, the feed — after its own run — starts ONE background thread with a vector_embed job row (target ring): unit crate@version, source = the crate's manifest directory from cargo metadata --offline in the consumer's clone, the whole job priced once by the embed budget, per-crate ledger rows, resumable. One embedding run at a time per process (fnvec::embed_turn: the feed for its run, the ring per crate). nornir workspace embed <ws> --ring runs it on request |
fnvec/ring.rs |
| lenses | one SymbolVec per function node with a vector: signature from PARAM nodes (receiver bare, others in name order — no return type, P1), callee names from CALLS, impl Trait for T / impl T from the block + IMPLEMENTS; chunk_hash = the chunk INSTANCE repo ␟ file ␟ start-end, so shares_one_chunk_vector refuses two nodes cut from the same lines (one source compiled into two targets) and keeps a byte-identical copy elsewhere. Detection is workspace-wide, repo restricts reporting |
mimir_lens/room_feed/vectors.rs |
| states | no_embedder · vectors_missing (new code_room::States: a refusal, no count in the answer) · partial with "vectors missing for N of M" / "vectors of seq a, room at seq b" · complete / empty_fact. Every computed answer carries vectors (functions, with a vector, distinct bodies, model, backend that embedded) and knn (device, kernel, comparisons) |
same |
| CLI | nornir workspace embed <ws> [--ring] embeds the functions of every consumed member first; nornir mimir analyze duplicates [--repo --cosine --structural --same-crate --keep-trait-impls], and --calibration [--around X] [--files a.rs,b.rs]: the threshold instrument (cosine distribution, what each threshold keeps, the pairs around the bar) |
src/bin/nornir.rs, room_feed/vectors.rs |
Measured (znippy-zoomies, dwarf, 2026-10-13)
Release build --no-default-features --features server,vector,embed-tract,mcp, scratch workspace, tract CPU, the box shared with three other lanes.
| what | number |
consume @ 66a2891 (driver, 608 crates) |
400 s, +3 242 285 rows; lake 916 MB |
function nodes (fn + method, local, defined) |
4 628: 3 546 chunks · 711 written by a macro · 371 in build-script output |
| embed (10 workers under a 4 GB cap) | 3 377 distinct bodies, 3 473 pieces, 0.77 function/s, 4 364 s (72.7 min) |
| tables | embeddings 16 MB · embedding_node_events 0.2 MB · embedding_node_runs 0.1 MB |
similar_code, CLI door |
complete, 100 clusters / 219 pairs, 18 families withheld, 27 same-lines pairs refused; 1.0–2.3 s |
semantic_clones, CLI door |
complete, 19 clusters / 74 pairs; 1.0–2.7 s |
both, server door (MCP → gRPC Mimir.*) |
the same answers; 1.2–5.0 s |
| next commit (two function bodies changed) | consume 58 s (58 crates re-dumped); function half: 2 vectors, 2 link events, 0.8 s |
| its ring (plan only) | 143 464 function-like nodes, 90 185 with a chunk in 408 crates, every source directory found |
Not measured there: the ring's price and its embed, the lenses through the server door after the second commit (through the CLI door they answered complete at seq 2 in 1.2–1.8 s), and the line-window half of the feed (never priced: stopped by hand after the function half). The server was stopped on the owner's order before those. With the default 10-minute embed budget the function half itself is refused on this CPU (priced at 30–100 min): the feed row is error and the lenses say vectors_missing with that reason.
The threshold (DupThresholds::cosine = 0.92, kept)
All pairs of the 3 546 vectors (6 285 258; mimir analyze duplicates --calibration): p50 0.204 · p90 0.381 · p99 0.561 · p99.9 0.750 · p99.99 0.931. At 0.92: 736 pairs, 211 of identical text; of the 525 others, 400 pass the structural gate and 97 are also in different crates (what the lens reports: 100 clusters with the identical ones).
Hand-checked against the source, cross-crate pairs that pass the structural gate:
| cosine band | pairs | true copy | near (same job, rewritten) | not a duplicate |
| top 20 (0.9825 – 0.9998) | 20 | 20 | 0 | 0 |
| 0.92 – 0.96 | 54 | 33 | 12 | 9 (5 of them len / is_empty: withheld as accessor families) |
| 0.90 – 0.92 | 31 | 4 | 5 | 22 (14 are three-line test stubs) |
0.92 is where the precision breaks, so it stays. What it does NOT find: real copies with systematic renames score far lower — ljar::main ≈ lzip::main (89 lines, 99 % the same text) 0.856, batch::fill_buffer (62 lines, 96 %) 0.813, batch::decompress_jars ≈ decompress_zips 0.866 — in a band (0.80 – 0.92) where 77 of the 91 pairs of ten lines or more share less than 85 % of their text. No cosine separates those; they are the sub-clone (text) lens's findings. Of the reference pair ljar.rs ≈ lzip.rs: decode_entry and physical_cores (identical) and local_data_offset (0.928) are reported, main is not.
Tests and red-proofs
cargo test --lib --no-default-features --features server,vector,viz (vector_tests, fnvec, room_feed, mimir_analyze, mimir_clones, mimir_calibrate, code_room, vector::, iceberg_schema, consumer_door_tests): 232 passed, 0 failed, 4 ignored. Guards no_second_parser 7/7, code_room_seam 5/5. cargo check green for server,vector,embed-tract,mcp, full-server, viz (no code-room).
| rule broken | tests that went red |
| embed every chunk every time (no dedupe, stored vectors ignored) | every_function_node_is_linked_and_an_identical_body_is_embedded_once (10 vectors for 7 bodies), a_commit_embeds_only_the_functions_whose_text_changed, fnvec::the_same_body_nested_deeper_has_the_same_content_hash |
shares_one_chunk_vector answers false |
similar_code_finds_the_copy_refuses_the_shared_lines_and_names_the_kernel (the shared lines become a cluster), semantic_clones_computes_over_the_same_vectors, the two predicate tests of mimir_analyze / mimir_clones |
| no vector at all is answered as a computed result | without_vectors_the_lenses_say_vectors_missing_and_carry_no_headline, a_feed_run_that_failed_…, a_feed_that_could_not_embed_… (empty_gap with counts instead of the refusal) |
| default cosine 0.93 / 0.91 | the_recorded_real_pairs_are_judged_as_a_person_judged_them (the copy at 0.928 lost / the non-duplicate at 0.916 reported) |
| the ring plan forgets what is done | the_ring_is_embedded_once_per_crate_version_and_a_bump_embeds_only_the_new_one |
NOT run: the default-feature cargo test --lib and cargo check (the box was needed elsewhere), and nothing on a GPU. --features vector-gpu did not compile on this tree (gpu.rs: wgpu 30 made get_mapped_range a Result); fixed in one line, compiled, its GPU tests skip on dwarf (no adapter).
What is not in this lane
- The ring's vectors are written and linked; no lens compares against them yet.
- The lenses' candidate scan is the exact all-pairs cosine on the CPU (
VectorIndex::pairs_above). The WebGPU kNN (vector/gpu.rs, top-8, int8) is untouched and not on the lens path: a threshold scan needs every pair above the bar. - Free-text search still reads line windows: Rust code is embedded twice (once per function, once per window).
- A driver that runs inside a toolchain image keeps its cargo registry there: the ring job then finds no source and writes
skippedrows naming the crates.