src/code_room/reader/wired/codemap.rs
//! `Viz.CodeMap` — **the Mímir graph as a map you fly over** (owner 2026-10-16:
//! "the parsing of many crates to a graph … I wanted the 3D room to be that
//! graph").
//!
//! The room is laid out ONCE per room version ([`MapLayout`], kept in this
//! process): repos as regions, their crates as plates inside them, and on every
//! plate the function CARDS of that crate — a head (the name) with what the
//! room knows of the function under it (its params, its return type, its
//! locals, the fields it reads) and its call sites in source order. The
//! dependencies the workspace calls are plates of their own, further out. The
//! layout is a pure function of the room — the same crate lands in the same
//! place every time the same room is asked — and a client only places what an
//! answer says: it never sees the whole room at once, it asks for what its
//! camera sees, by level of detail:
//!
//! | `lod` | answer |
//! |----------|--------|
//! | `world` | repos and crates with their rects, crate → crate edges bundled with counts, the tiles that hold cards |
//! | `modules`| a plate up close (`crate`): its module blocks, each with its name, its cards and the types it defines |
//! | `cards` | the cards of ONE tile (`tile: [ix, iz]`, [`TILE`] world units square): rows and call sites, each site with where its callee's head is |
//! | `find` | crates and functions whose name holds `q`, with where they are |
//! | `calls` | a head expanded (`card`): every function it calls and every caller, with where their heads are |
//! | `source` | the lines of one function (`moniker`) from the commit the room was read at, a page at a time (`from`, `max_lines`) |
//! | `commits`| the consumed commits of one `repo` (seq, sha, subject), oldest first — the timeline the diff is picked from |
//! | `diff` | what changed in `repo` between seqs `from` and `to` (default: the tip), placed on THIS map: added / removed / changed fn heads (removed ones on ghost spots below their plate), the CALLS splines among them, per-plate counts — from the READ-FAST zone's interval tables |
//! | `source_at` | the lines of one `file` at one commit (`repo`, `sha`) — the diff hover's before/after |
//! | `trace_runs` / `trace` / `profile` | a traced run replayed on this map: its runs, a window of its events on heads, its heat ([`super::trace`]) |
//!
//! Each answer is a queued surface ([`super::super::queued`]): computed on the
//! jera rooms lane and kept per (room version, request).
//!
//! World units: x to the right, z down the map (the plane), y up. A card's
//! `rect` is `[x0, z0, x1, z1]`; its head is the band `z0 .. z0 + head_h`, then
//! one row of `row_h` per attribute, then one per call site. A site's marker is
//! on the card's right edge at its row; its callee's head is `to_at` (the top
//! middle of the callee's card).
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use mimir_room::{EdgeSel, GroupBy, MaskSpec, Room};
use mimir_schema::{EdgeKind, Origin, kind as nk};
use serde_json::{Value, json};
use super::{LOWER_BOUND, envelope, internal, invalid, miss, note, obj, open, settle, text};
use crate::code_room::{CodeRoom, Reach, Refusal};
/// A card's width.
pub const CARD_W: f32 = 8.0;
/// The head band of a card (the function's name).
pub const HEAD_H: f32 = 1.2;
/// One row under the head (an attribute, a call site).
pub const ROW_H: f32 = 0.6;
/// Below a card's last row.
pub const CARD_PAD: f32 = 0.3;
/// Between two cards of a column / two columns.
pub const GAP_Z: f32 = 1.5;
pub const GAP_X: f32 = 2.5;
/// Around a crate's cards, and the band its name lies on.
pub const CRATE_MARGIN: f32 = 3.0;
pub const CRATE_TITLE: f32 = 5.0;
/// A plate's (or a region's) name band grows with it, so a big crate's name reads from
/// as far as the crate itself does: `min` at least, a share of the width, `max` at most.
pub fn title_band(width: f32, min: f32, max: f32) -> f32 {
(width * 0.06).clamp(min, max)
}
/// Around a module's cards, the band its name lies on, between two modules.
pub const MODULE_MARGIN: f32 = 1.5;
pub const MODULE_TITLE: f32 = 3.0;
pub const MODULE_GAP: f32 = 2.0;
/// Type names a module block lists at most.
pub const MODULE_TYPES_MAX: usize = 12;
/// Around a repo's crates, and the band its name lies on.
pub const REPO_MARGIN: f32 = 8.0;
pub const REPO_TITLE: f32 = 12.0;
/// Attribute rows a card shows at most (the rest are counted: `more_rows`).
pub const ATTR_ROWS_MAX: usize = 10;
/// Call-site rows a card shows at most (one per callee; the rest: `more_sites`).
pub const SITE_ROWS_MAX: usize = 12;
/// The side of a card tile.
pub const TILE: f32 = 128.0;
/// Matches a `find` returns at most.
const FIND_MAX: usize = 40;
/// Lines of a `source` page when the request names none.
const SOURCE_PAGE: u64 = 200;
/// A rect `[x0, z0, x1, z1]`.
pub type Rect = [f32; 4];
/// One crate plate.
#[derive(Debug, Clone)]
pub struct CratePlate {
/// `name@version`.
pub key: String,
pub name: String,
pub version: String,
/// The consumed repo that defines it; `None` for a dependency.
pub repo: Option<String>,
pub local: bool,
pub rect: Rect,
/// The height of the band its name lies on (at its top).
pub title: f32,
/// Cards on it.
pub cards: usize,
}
/// One module's block on its crate's plate: its cards, under its name band.
#[derive(Debug, Clone, Copy)]
pub struct ModuleBlock {
pub krate: u32,
/// The module (or crate root) node; `None` for a dependency's heads.
pub node: Option<u64>,
pub rect: Rect,
pub cards: usize,
}
/// One function card's place.
#[derive(Debug, Clone, Copy)]
pub struct Card {
pub node: u64,
pub krate: u32,
pub rect: Rect,
/// Defined by a consumed repo (`false`: a function they call elsewhere, a head only).
pub home: bool,
/// Its height above the plane (the role layout's third axis; 0 by crate).
pub y: f32,
}
/// **The map of one room version.** Pure: [`MapLayout::of`] of the same room
/// is the same map.
#[derive(Debug, Clone, Default)]
pub struct MapLayout {
pub version: u64,
/// (name, rect, the height of its name band)
pub repos: Vec<(String, Rect, f32)>,
pub crates: Vec<CratePlate>,
pub cards: Vec<Card>,
/// Module blocks, plate by plate.
pub modules: Vec<ModuleBlock>,
/// node ordinal → its card
pub card_of: HashMap<u64, u32>,
/// tile → its cards (by the card's centre), in card order
pub tiles: BTreeMap<(i32, i32), Vec<u32>>,
/// The crate quotient's bundled edges: (from plate, to plate) → (depends_on, calls, call_sites).
pub edges: BTreeMap<(u32, u32), (u64, u64, u64)>,
pub bounds: Rect,
/// `crate` (plates in regions) or `role` ([`MapLayout::by_role`]).
pub layout: &'static str,
/// The role vectors' space (role layout only).
pub space: Option<String>,
}
/// A card's height for its attribute and call-site row counts.
pub fn card_height(attrs: usize, sites: usize) -> f32 {
let rows = attrs.min(ATTR_ROWS_MAX) + usize::from(attrs > ATTR_ROWS_MAX) + sites.min(SITE_ROWS_MAX) + usize::from(sites > SITE_ROWS_MAX);
HEAD_H + rows as f32 * ROW_H + CARD_PAD
}
/// The tile a point lies in.
pub fn tile_of(x: f32, z: f32) -> (i32, i32) {
((x / TILE).floor() as i32, (z / TILE).floor() as i32)
}
/// **Column packing** of cards (heights) into a block: columns of [`CARD_W`],
/// each filled top to bottom until it is about as tall as the block is wide.
/// Returns each card's `(x, z)` offset and the block's `(w, d)`.
pub fn pack_columns(heights: &[f32]) -> (Vec<(f32, f32)>, (f32, f32)) {
if heights.is_empty() {
return (Vec::new(), (CARD_W, HEAD_H));
}
let area: f32 = heights.iter().map(|h| (h + GAP_Z) * (CARD_W + GAP_X)).sum();
let target = area.sqrt().max(heights.iter().copied().fold(0.0, f32::max));
let (mut at, mut x, mut z, mut deepest) = (Vec::with_capacity(heights.len()), 0.0f32, 0.0f32, 0.0f32);
for &h in heights {
if z > 0.0 && z + h > target {
x += CARD_W + GAP_X;
z = 0.0;
}
at.push((x, z));
z += h + GAP_Z;
deepest = deepest.max(z - GAP_Z);
}
(at, (x + CARD_W, deepest))
}
/// **Shelf packing** of blocks `(w, d)`: tallest first (ties by the given
/// order), rows about as wide as the whole is deep. Returns each block's
/// `(x, z)` and the total `(w, d)`.
pub fn pack_shelves(sizes: &[(f32, f32)], gap: f32) -> (Vec<(f32, f32)>, (f32, f32)) {
if sizes.is_empty() {
return (Vec::new(), (0.0, 0.0));
}
let mut order: Vec<usize> = (0..sizes.len()).collect();
order.sort_by(|a, b| sizes[*b].1.total_cmp(&sizes[*a].1).then(a.cmp(b)));
let area: f32 = sizes.iter().map(|(w, d)| (w + gap) * (d + gap)).sum();
let width = area.sqrt().max(sizes.iter().map(|s| s.0).fold(0.0, f32::max));
let mut at = vec![(0.0, 0.0); sizes.len()];
let (mut x, mut z, mut shelf, mut widest) = (0.0f32, 0.0f32, 0.0f32, 0.0f32);
for i in order {
let (w, d) = sizes[i];
if x > 0.0 && x + w > width {
z += shelf + gap;
x = 0.0;
shelf = 0.0;
}
at[i] = (x, z);
x += w + gap;
widest = widest.max(x - gap);
shelf = shelf.max(d);
}
(at, (widest, z + shelf))
}
fn crate_key(krate: &str, version: Option<&str>) -> String {
format!("{krate}@{}", version.unwrap_or(""))
}
impl MapLayout {
/// **Lay the room out.** Crates are the room's crate quotient; each local
/// fn / method is a card on its crate's plate, in source order (file,
/// line); every function they call that is no such card (a dependency's,
/// a stub) is a head-only card on its own crate's plate.
pub fn of(room: &Room) -> anyhow::Result<MapLayout> {
let quotient = room.quotient(GroupBy::Crate, &[EdgeKind::DependsOn], None)?.value;
let calls_q = room.quotient(GroupBy::Crate, &[EdgeKind::Calls], None)?.value;
let fns = room
.mask(&MaskSpec { origins: vec![Origin::Local], defined_only: true, node_kinds: vec![nk::FN.into(), nk::METHOD.into()], ..Default::default() })?
.value;
// rows each card will hold: attributes (params, ret, locals, fields read) and callees
let mut attrs: HashMap<u64, usize> = HashMap::new();
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Contains], from: Some(&fns), ..Default::default() })?.value.edges {
if matches!(room.node(e.to).kind, Some(nk::PARAM | nk::RET | nk::LOCAL)) {
*attrs.entry(e.from).or_default() += 1;
}
}
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::ReadsField], from: Some(&fns), ..Default::default() })?.value.edges {
*attrs.entry(e.from).or_default() += 1;
}
let mut callees: HashMap<u64, usize> = HashMap::new();
let mut called: Vec<u64> = Vec::new();
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&fns), ..Default::default() })?.value.edges {
*callees.entry(e.from).or_default() += 1;
if !fns.contains(e.to) {
called.push(e.to);
}
}
called.sort_unstable();
called.dedup();
// plates: every crate group, then any crate a card names that no group has
let mut crates: Vec<CratePlate> = Vec::new();
let mut by_key: HashMap<String, u32> = HashMap::new();
for g in "ient.groups {
let (name, version) = g.key.rsplit_once('@').unwrap_or((g.key.as_str(), ""));
let own = g.node.map(|n| room.node(n));
let local = own.as_ref().is_some_and(|v| v.defined && v.origin == Some(Origin::Local));
let repo = own.as_ref().and_then(|v| v.repo).filter(|_| local).map(str::to_string);
by_key.insert(g.key.clone(), crates.len() as u32);
crates.push(CratePlate { key: g.key.clone(), name: name.to_string(), version: version.to_string(), repo, local, rect: [0.0; 4], title: CRATE_TITLE, cards: 0 });
}
let mut plate_of = |room: &Room, o: u64, crates: &mut Vec<CratePlate>| -> Option<u32> {
let v = room.node(o);
let k = v.krate?;
let key = crate_key(k, v.version);
Some(*by_key.entry(key.clone()).or_insert_with(|| {
let local = v.origin == Some(Origin::Local);
crates.push(CratePlate { key, name: k.to_string(), version: v.version.unwrap_or("").to_string(), repo: v.repo.filter(|_| local).map(str::to_string), local, rect: [0.0; 4], title: CRATE_TITLE, cards: 0 });
crates.len() as u32 - 1
}))
};
// cards per plate, in source order
let mut on_plate: Vec<Vec<(u64, f32, bool)>> = Vec::new();
let mut sorted: Vec<u64> = fns.ordinals().to_vec();
sorted.sort_by(|a, b| {
let (x, y) = (room.node(*a), room.node(*b));
(x.file.unwrap_or("~"), x.start_line.unwrap_or(u32::MAX), x.moniker).cmp(&(y.file.unwrap_or("~"), y.start_line.unwrap_or(u32::MAX), y.moniker))
});
let mut stubs: Vec<u64> = called;
stubs.sort_by(|a, b| (room.node(*a).name, room.moniker(*a)).cmp(&(room.node(*b).name, room.moniker(*b))));
for (list, home) in [(&sorted, true), (&stubs, false)] {
for &o in list.iter() {
let Some(p) = plate_of(room, o, &mut crates) else { continue };
if on_plate.len() < crates.len() {
on_plate.resize(crates.len(), Vec::new());
}
let h = if home { card_height(attrs.get(&o).copied().unwrap_or(0), callees.get(&o).copied().unwrap_or(0)) } else { HEAD_H };
on_plate[p as usize].push((o, h, home));
}
}
on_plate.resize(crates.len(), Vec::new());
// each plate: its cards grouped by module (in source order), each module a
// block of columns under its name band, the blocks shelved on the plate
// inside its margin and under the crate's name band
let mut plate_size: Vec<(f32, f32)> = Vec::with_capacity(crates.len());
let mut plate_band: Vec<f32> = Vec::with_capacity(crates.len());
// per plate: its module blocks (module node, the cards' indexes into on_plate, offset, size)
let mut plate_blocks: Vec<Vec<(Option<u64>, Vec<usize>, (f32, f32), (f32, f32))>> = Vec::with_capacity(crates.len());
let mut card_off: Vec<Vec<(f32, f32)>> = Vec::with_capacity(crates.len());
for cards in &on_plate {
let mut order: Vec<Option<u64>> = Vec::new();
let mut members: HashMap<Option<u64>, Vec<usize>> = HashMap::new();
for (k, c) in cards.iter().enumerate() {
let m = if c.2 { room.module_of(c.0) } else { None };
members.entry(m).or_insert_with(|| {
order.push(m);
Vec::new()
});
members.get_mut(&m).expect("just made").push(k);
}
let mut off = vec![(0.0f32, 0.0f32); cards.len()];
let mut blocks: Vec<(Option<u64>, Vec<usize>, (f32, f32), (f32, f32))> = Vec::new();
for m in order {
let ks = members.remove(&m).unwrap_or_default();
let heights: Vec<f32> = ks.iter().map(|k| cards[*k].1).collect();
let (at, (w, d)) = pack_columns(&heights);
for (k, a) in ks.iter().zip(at) {
off[*k] = (MODULE_MARGIN + a.0, MODULE_MARGIN + MODULE_TITLE + a.1);
}
blocks.push((m, ks, (0.0, 0.0), (w + 2.0 * MODULE_MARGIN, d + 2.0 * MODULE_MARGIN + MODULE_TITLE)));
}
let sizes: Vec<(f32, f32)> = blocks.iter().map(|b| b.3).collect();
let (at, (w, d)) = pack_shelves(&sizes, MODULE_GAP);
for (b, a) in blocks.iter_mut().zip(at) {
b.2 = a;
for k in &b.1 {
off[*k] = (off[*k].0 + a.0, off[*k].1 + a.1);
}
}
let band = title_band(w.max(CARD_W), CRATE_TITLE, 40.0);
plate_size.push((w.max(CARD_W) + 2.0 * CRATE_MARGIN, d + 2.0 * CRATE_MARGIN + band));
plate_band.push(band);
plate_blocks.push(blocks);
card_off.push(off);
}
// regions: each consumed repo, then the dependencies, in name order
let mut regions: BTreeMap<(bool, String), Vec<u32>> = BTreeMap::new();
for (i, c) in crates.iter().enumerate() {
let region = if c.local { (false, c.repo.clone().unwrap_or_default()) } else { (true, "dependencies".to_string()) };
regions.entry(region).or_default().push(i as u32);
}
let mut region_size: Vec<(f32, f32)> = Vec::new();
let mut region_band: Vec<f32> = Vec::new();
let mut region_off: Vec<Vec<(f32, f32)>> = Vec::new();
for members in regions.values() {
let sizes: Vec<(f32, f32)> = members.iter().map(|i| plate_size[*i as usize]).collect();
let (at, (w, d)) = pack_shelves(&sizes, CRATE_MARGIN * 2.0);
let band = title_band(w, REPO_TITLE, 160.0);
region_size.push((w + 2.0 * REPO_MARGIN, d + 2.0 * REPO_MARGIN + band));
region_band.push(band);
region_off.push(at);
}
// the consumed repos together; the dependencies to their right, further out
let n_home = regions.keys().filter(|k| !k.0).count();
let (home_at, (home_w, _)) = pack_shelves(®ion_size[..n_home], REPO_MARGIN * 2.0);
let mut region_at: Vec<(f32, f32)> = home_at;
let mut dep_x = home_w + REPO_MARGIN * 8.0;
for s in ®ion_size[n_home..] {
region_at.push((dep_x, 0.0));
dep_x += s.0 + REPO_MARGIN * 2.0;
}
let mut map = MapLayout { version: room.version(), layout: "crate", ..Default::default() };
for (r, ((key, members), (rx, rz))) in regions.iter().zip(region_at.iter().copied()).enumerate() {
let (rw, rd) = region_size[r];
map.repos.push((key.1.clone(), [rx, rz, rx + rw, rz + rd], region_band[r]));
for (m, &ci) in members.iter().enumerate() {
let (ox, oz) = region_off[r][m];
let (px, pz) = (rx + REPO_MARGIN + ox, rz + REPO_MARGIN + region_band[r] + oz);
let (pw, pd) = plate_size[ci as usize];
crates[ci as usize].rect = [px, pz, px + pw, pz + pd];
crates[ci as usize].title = plate_band[ci as usize];
crates[ci as usize].cards = on_plate[ci as usize].len();
let (ix, iz) = (px + CRATE_MARGIN, pz + CRATE_MARGIN + plate_band[ci as usize]);
for (m, ks, (bx, bz), (bw, bd)) in &plate_blocks[ci as usize] {
map.modules.push(ModuleBlock { krate: ci, node: *m, rect: [ix + bx, iz + bz, ix + bx + bw, iz + bz + bd], cards: ks.len() });
}
for (k, &(o, h, home)) in on_plate[ci as usize].iter().enumerate() {
let (cx, cz) = card_off[ci as usize][k];
let (x0, z0) = (ix + cx, iz + cz);
let id = map.cards.len() as u32;
let rect = [x0, z0, x0 + CARD_W, z0 + h];
map.cards.push(Card { node: o, krate: ci, rect, home, y: 0.0 });
map.card_of.insert(o, id);
map.tiles.entry(tile_of((rect[0] + rect[2]) * 0.5, (rect[1] + rect[3]) * 0.5)).or_default().push(id);
}
}
}
map.bounds = map.repos.iter().fold([f32::MAX, f32::MAX, f32::MIN, f32::MIN], |b, (_, r, _)| [b[0].min(r[0]), b[1].min(r[1]), b[2].max(r[2]), b[3].max(r[3])]);
// the crate quotient's edges, onto the plates (the groups of both quotients are the plates' first entries)
let group_plate = |q: &mimir_room::Quotient, g: usize| by_key.get(&q.groups[g].key).copied();
for e in "ient.edges {
if let (Some(f), Some(t)) = (group_plate("ient, e.from), group_plate("ient, e.to)) {
map.edges.entry((f, t)).or_default().0 += e.edges;
}
}
for e in &calls_q.edges {
if let (Some(f), Some(t)) = (group_plate(&calls_q, e.from), group_plate(&calls_q, e.to)) {
let s = map.edges.entry((f, t)).or_default();
s.1 += e.edges;
s.2 += e.sites;
}
}
map.crates = crates;
Ok(map)
}
/// The top middle of a card: where splines to it land.
pub fn head_at(&self, card: u32) -> [f32; 2] {
let r = self.cards[card as usize].rect;
[(r[0] + r[2]) * 0.5, r[1]]
}
}
// ------------------------------------------------------------------ the role layout
/// How high the role layout's third axis lifts a card (± world units).
pub const ROLE_Y: f32 = 14.0;
/// The role vectors of the room's own functions, by node ordinal. The clone
/// signals' `role_space` (space `fastrp-v1`, 128 dims, name-free) is asked
/// first in [`layout_of`]; this is the FALLBACK when it has no vectors — a
/// name-free structural stand-in (`structure-stub-v0`): per function its fan-out, fan-in,
/// call sites, params, return, locals, fields read, types used, body length,
/// method or not, and where its callees live (this crate, another of the
/// workspace, a dependency) — each column standardised, each row L2-normalised.
pub struct RoleVectors {
pub space: String,
pub by_node: HashMap<u64, Vec<f32>>,
}
pub fn role_vectors(room: &Room, map: &MapLayout) -> anyhow::Result<RoleVectors> {
let homes: Vec<u64> = map.cards.iter().filter(|c| c.home).map(|c| c.node).collect();
let mask = room.mask_of(&homes);
let idx: HashMap<u64, usize> = homes.iter().enumerate().map(|(i, o)| (*o, i)).collect();
const D: usize = 13;
let mut f = vec![[0f32; D]; homes.len()];
let krate_of = |o: u64| map.card_of.get(&o).map(|c| map.cards[*c as usize].krate);
let mut callees: Vec<(usize, usize, usize)> = vec![(0, 0, 0); homes.len()];
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&mask), ..Default::default() })?.value.edges {
let Some(&i) = idx.get(&e.from) else { continue };
f[i][0] += 1.0;
f[i][2] += e.count as f32;
let (a, b) = (krate_of(e.from), krate_of(e.to));
let c = &mut callees[i];
match (a, b) {
(Some(a), Some(b)) if a == b => c.0 += 1,
(_, Some(b)) if map.crates[b as usize].local => c.1 += 1,
_ => c.2 += 1,
}
}
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Calls], to: Some(&mask), ..Default::default() })?.value.edges {
if let Some(&i) = idx.get(&e.to) {
f[i][1] += 1.0;
}
}
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Contains], from: Some(&mask), ..Default::default() })?.value.edges {
let Some(&i) = idx.get(&e.from) else { continue };
match room.node(e.to).kind {
Some(nk::PARAM) => f[i][3] += 1.0,
Some(nk::RET) => f[i][4] = 1.0,
Some(nk::LOCAL) => f[i][5] += 1.0,
_ => {}
}
}
for (k, col) in [(EdgeKind::ReadsField, 6usize), (EdgeKind::UsesType, 7)] {
for e in room.edges(&EdgeSel { kinds: vec![k], from: Some(&mask), ..Default::default() })?.value.edges {
if let Some(&i) = idx.get(&e.from) {
f[i][col] += 1.0;
}
}
}
for (i, o) in homes.iter().enumerate() {
let v = room.node(*o);
f[i][8] = match (v.start_line, v.end_line) {
(Some(a), Some(b)) if b >= a => (b - a + 1) as f32,
_ => 0.0,
};
f[i][9] = f32::from(u8::from(v.kind == Some(nk::METHOD)));
let (same, other, dep) = callees[i];
let n = (same + other + dep).max(1) as f32;
(f[i][10], f[i][11], f[i][12]) = (same as f32 / n, other as f32 / n, dep as f32 / n);
for c in [0, 1, 2, 5, 6, 7, 8] {
f[i][c] = f[i][c].ln_1p();
}
}
// standardise each column, then each row to unit length
let n = f.len().max(1) as f32;
for c in 0..D {
let mean = f.iter().map(|r| r[c]).sum::<f32>() / n;
let sd = (f.iter().map(|r| (r[c] - mean).powi(2)).sum::<f32>() / n).sqrt().max(1e-6);
for r in f.iter_mut() {
r[c] = (r[c] - mean) / sd;
}
}
let by_node = homes
.iter()
.zip(f)
.map(|(o, r)| {
let l = r.iter().map(|x| x * x).sum::<f32>().sqrt().max(1e-6);
(*o, r.iter().map(|x| x / l).collect())
})
.collect();
Ok(RoleVectors { space: "structure-stub-v0".into(), by_node })
}
/// **PCA to three axes**: the rows centred, the covariance's three leading
/// eigenvectors by power iteration with deflation (a fixed start, each sign fixed
/// so its largest entry is positive — the same rows give the same axes).
pub fn pca3(rows: &[Vec<f32>]) -> Vec<[f32; 3]> {
let Some(d) = rows.first().map(Vec::len) else { return Vec::new() };
let n = rows.len() as f64;
let mut mean = vec![0f64; d];
for r in rows {
for (m, x) in mean.iter_mut().zip(r) {
*m += f64::from(*x) / n;
}
}
let mut cov = vec![0f64; d * d];
let mut c = vec![0f64; d];
for r in rows {
for k in 0..d {
c[k] = f64::from(r[k]) - mean[k];
}
for a in 0..d {
let ca = c[a];
if ca == 0.0 {
continue;
}
for b in a..d {
cov[a * d + b] += ca * c[b];
}
}
}
for a in 0..d {
for b in 0..a {
cov[a * d + b] = cov[b * d + a];
}
}
let mut axes: Vec<Vec<f64>> = Vec::new();
for k in 0..3usize.min(d) {
let mut v: Vec<f64> = (0..d).map(|i| 1.0 + ((i * 7 + k * 13) % 11) as f64 * 0.01).collect();
for _ in 0..200 {
let mut w = vec![0f64; d];
for a in 0..d {
w[a] = (0..d).map(|b| cov[a * d + b] * v[b]).sum();
}
for u in &axes {
let p: f64 = w.iter().zip(u).map(|(x, y)| x * y).sum();
for (x, y) in w.iter_mut().zip(u) {
*x -= p * y;
}
}
let l = w.iter().map(|x| x * x).sum::<f64>().sqrt();
if l < 1e-12 {
break;
}
v = w.iter().map(|x| x / l).collect();
}
let big = v.iter().copied().fold(0f64, |m, x| if x.abs() > m.abs() { x } else { m });
if big < 0.0 {
v.iter_mut().for_each(|x| *x = -*x);
}
axes.push(v);
}
rows.iter()
.map(|r| {
let mut p = [0f32; 3];
for (k, u) in axes.iter().enumerate() {
p[k] = r.iter().zip(&mean).zip(u).map(|((x, m), w)| (f64::from(*x) - m) * w).sum::<f64>() as f32;
}
p
})
.collect()
}
impl MapLayout {
/// **The map by ROLE**: the same cards (the same ids), placed by where their
/// role vector projects (`coords`, PCA): the first two axes on the plane —
/// snapped to a grid of card cells, the nearest free cell — the third as
/// height. A card with no vector (a dependency's head) stands in rows beyond.
/// Crates are a tint now: no plates, no regions, no modules.
pub fn by_role(&self, coords: &HashMap<u64, [f32; 3]>, space: &str) -> MapLayout {
let cell_w = CARD_W + GAP_X;
let cell_h = card_height(ATTR_ROWS_MAX + 1, SITE_ROWS_MAX + 1) + GAP_Z;
let placed: Vec<usize> = self.cards.iter().enumerate().filter(|(_, c)| coords.contains_key(&c.node)).map(|(i, _)| i).collect();
let g = ((placed.len() as f32).sqrt() * 1.3).ceil().max(1.0) as i32;
// a robust scale per axis: the 98th percentile of |coordinate|
let scale = |k: usize| {
let mut a: Vec<f32> = placed.iter().map(|i| coords[&self.cards[*i].node][k].abs()).collect();
a.sort_by(f32::total_cmp);
a.get((a.len() as f32 * 0.98) as usize).or(a.last()).copied().unwrap_or(1.0).max(1e-6)
};
let (s0, s1, s2) = (scale(0), scale(1), scale(2));
let mut out = MapLayout { version: self.version, crates: self.crates.clone(), cards: self.cards.clone(), card_of: self.card_of.clone(), edges: self.edges.clone(), layout: "role", space: Some(space.to_string()), ..Default::default() };
for c in &mut out.crates {
c.rect = [0.0; 4];
}
let mut taken: std::collections::HashSet<(i32, i32)> = std::collections::HashSet::with_capacity(placed.len() * 2);
let cell_of = |v: f32, sc: f32| ((((v / sc).clamp(-1.0, 1.0) + 1.0) * 0.5) * (g - 1) as f32).round() as i32;
for &i in &placed {
let p = coords[&self.cards[i].node];
let want = (cell_of(p[0], s0), cell_of(p[1], s1));
let at = nearest_free(&taken, want);
taken.insert(at);
let h = self.cards[i].rect[3] - self.cards[i].rect[1];
let (x0, z0) = (at.0 as f32 * cell_w, at.1 as f32 * cell_h);
out.cards[i].rect = [x0, z0, x0 + CARD_W, z0 + h];
out.cards[i].y = (p[2] / s2).clamp(-1.0, 1.0) * ROLE_Y;
}
// the rest: in rows beyond the role field, crate by crate
let mut rest: Vec<usize> = (0..self.cards.len()).filter(|i| !coords.contains_key(&self.cards[*i].node)).collect();
rest.sort_by_key(|i| (self.cards[*i].krate, *i));
let (base_z, per_row) = ((g as f32 + 4.0) * cell_h, g.max(8) as usize);
for (k, &i) in rest.iter().enumerate() {
// the row pitch fits the tallest card, like the role grid: rows never overlap
let (cx, cz) = ((k % per_row) as f32 * cell_w, base_z + (k / per_row) as f32 * cell_h);
let h = self.cards[i].rect[3] - self.cards[i].rect[1];
out.cards[i].rect = [cx, cz, cx + CARD_W, cz + h];
out.cards[i].y = 0.0;
}
for (id, c) in out.cards.iter().enumerate() {
out.tiles.entry(tile_of((c.rect[0] + c.rect[2]) * 0.5, (c.rect[1] + c.rect[3]) * 0.5)).or_default().push(id as u32);
}
out.bounds = out.cards.iter().fold([f32::MAX, f32::MAX, f32::MIN, f32::MIN], |b, c| [b[0].min(c.rect[0]), b[1].min(c.rect[1]), b[2].max(c.rect[2]), b[3].max(c.rect[3])]);
if out.cards.is_empty() {
out.bounds = [0.0; 4];
}
out
}
}
/// The free grid cell nearest `want` (ring by ring, a fixed order in each ring).
fn nearest_free(taken: &std::collections::HashSet<(i32, i32)>, want: (i32, i32)) -> (i32, i32) {
if !taken.contains(&want) {
return want;
}
for r in 1i32.. {
let mut best: Option<((i32, i32), i32)> = None;
for dx in -r..=r {
for dz in [-r, r] {
for (x, z) in [(want.0 + dx, want.1 + dz), (want.0 + dz, want.1 + dx)] {
if !taken.contains(&(x, z)) {
let d = (x - want.0).pow(2) + (z - want.1).pow(2);
if best.is_none_or(|b| d < b.1 || (d == b.1 && (x, z) < b.0)) {
best = Some(((x, z), d));
}
}
}
}
}
if let Some((c, _)) = best {
return c;
}
}
unreachable!("an infinite grid has a free cell")
}
/// The map of this room, laid out once per room version (and workspace) in this process.
fn layout_of(workspace: &str, room: &Room, wh: Option<&crate::warehouse::iceberg::IcebergWarehouse>, mode: &'static str) -> Result<Arc<MapLayout>, Refusal> {
static KEPT: Mutex<Vec<(String, u64, &'static str, Arc<MapLayout>)>> = Mutex::new(Vec::new());
let v = room.version();
if let Some((_, _, _, m)) = KEPT.lock().unwrap_or_else(|e| e.into_inner()).iter().find(|(w, x, l, _)| w == workspace && *x == v && *l == mode) {
return Ok(m.clone());
}
let m = if mode == "role" {
let base = layout_of(workspace, room, wh, "crate")?;
// The clone signals' role space first (fastrp-v1, stored vectors reused);
// without one (no vectors yet, or it refuses) the structural stand-in.
let rv = match crate::clone_signals::structure::role_space(room, wh, false) {
Ok(rs) if !rs.vectors.is_empty() => RoleVectors {
space: rs.space.to_string(),
by_node: rs.vectors.into_iter().map(|v| (v.ordinal, v.vector)).collect(),
},
_ => role_vectors(room, &base).map_err(internal)?,
};
let nodes: Vec<u64> = base.cards.iter().filter(|c| rv.by_node.contains_key(&c.node)).map(|c| c.node).collect();
let rows: Vec<Vec<f32>> = nodes.iter().map(|o| rv.by_node[o].clone()).collect();
let coords: HashMap<u64, [f32; 3]> = nodes.into_iter().zip(pca3(&rows)).collect();
Arc::new(base.by_role(&coords, &rv.space))
} else {
Arc::new(MapLayout::of(room).map_err(internal)?)
};
let mut kept = KEPT.lock().unwrap_or_else(|e| e.into_inner());
kept.retain(|(w, x, l, _)| !(w == workspace && *x == v && *l == mode));
kept.push((workspace.to_string(), v, mode, m.clone()));
if kept.len() > 6 {
kept.remove(0);
}
Ok(m)
}
fn rect_json(r: Rect) -> Value {
json!([r[0], r[1], r[2], r[3]])
}
/// `Viz.CodeMap` (see the module docs).
pub fn code_map(reach: &Reach<'_>, p: &Value) -> Result<Value, Refusal> {
let handle = reach.code_room()?;
let room = open(reach, "")?;
let lod = match text(p, "lod") {
"" => "world",
l => l,
};
if lod == "source" {
return source_page(reach, p);
}
if lod == "source_at" {
return source_at(handle, p);
}
if lod == "commits" {
return commits(handle, &room, p);
}
if lod == "trace_runs" {
return super::trace::runs(reach, p);
}
// `layout`: `crate` (plates in regions, the default) or `role` (placed by what the function does)
let mode = if text(p, "layout") == "role" { "role" } else { "crate" };
let map = layout_of(handle.workspace(), &room, reach.warehouse, mode)?;
let body = match lod {
"world" => world(&map),
"diff" => diff(reach, &room, &map, p)?,
"cards" => {
let t = p.get("tile").and_then(Value::as_array).ok_or_else(|| invalid("`tile` is `[ix, iz]` for lod `cards`"))?;
let at = |i: usize| t.get(i).and_then(Value::as_i64).map(|n| n as i32);
let (Some(ix), Some(iz)) = (at(0), at(1)) else { return Err(invalid("`tile` is `[ix, iz]` (integers)")) };
cards(handle, &room, &map, (ix, iz))?
}
"find" => find(&room, &map, text(p, "q"))?,
"trace" => return super::trace::window(reach, &room, &map, p),
"profile" => return super::trace::profile(reach, &room, &map, p),
"modules" => {
let krate = p.get("crate").and_then(Value::as_u64).ok_or_else(|| invalid("`crate` (a plate index) for lod `modules`"))?;
modules(&room, &map, krate)?
}
"calls" => {
let card = p.get("card").and_then(Value::as_u64).ok_or_else(|| invalid("`card` (a card index) for lod `calls`"))?;
calls(&room, &map, card)?
}
other => return Err(invalid(format!("`lod` `{other}`: one of world, modules, cards, find, calls, source, diff, commits, source_at, trace_runs, trace, profile"))),
};
let mut ev = room.mask(&MaskSpec { node_kinds: vec![nk::FN.into(), nk::METHOD.into()], origins: vec![Origin::Local], defined_only: true, ..Default::default() }).map_err(internal)?.evidence;
ev.missing.retain(|m| !m.input.starts_with("node kind"));
ev.query = format!("code map, {lod}");
note(
&mut ev,
"laid out once per room version: repos as regions, their crates as plates, every local fn / method a card in source order, \
every function they call elsewhere a head on its own crate's plate; the dependencies stand to the right",
);
note(&mut ev, LOWER_BOUND);
let empty = body.get("cards").and_then(Value::as_array).is_some_and(Vec::is_empty) && lod == "cards";
settle(&mut ev, empty || map.crates.is_empty());
let mut body = envelope(body, &ev);
body["room"] = super::panes::identity(handle, &room);
Ok(body)
}
fn world(map: &MapLayout) -> Value {
let repos: Vec<Value> = map.repos.iter().map(|(n, r, t)| json!({ "name": n, "rect": rect_json(*r), "title": t })).collect();
let crates: Vec<Value> = map
.crates
.iter()
.enumerate()
.map(|(i, c)| json!({ "id": c.key, "name": c.name, "version": c.version, "repo": c.repo, "local": c.local, "rect": rect_json(c.rect), "title": c.title, "cards": c.cards, "modules": map.modules.iter().filter(|m| m.krate as usize == i).count() }))
.collect();
let edges: Vec<Value> = map.edges.iter().filter(|((f, t), _)| f != t).map(|((f, t), (d, c, s))| json!([f, t, d, c, s])).collect();
let tiles: Vec<Value> = map.tiles.iter().map(|((x, z), v)| json!([x, z, v.len()])).collect();
// by role there are no plates: each tile says which crate most of its cards are of
let tints: Vec<Value> = if map.layout == "role" {
map.tiles
.iter()
.map(|((x, z), v)| {
let mut by: HashMap<u32, usize> = HashMap::new();
for c in v {
*by.entry(map.cards[*c as usize].krate).or_default() += 1;
}
let top = by.into_iter().max_by_key(|(k, n)| (*n, std::cmp::Reverse(*k))).map(|x| x.0).unwrap_or(0);
json!([x, z, top, v.len()])
})
.collect()
} else {
Vec::new()
};
obj([
("lod", json!("world")),
("layout", json!(map.layout)),
("space", json!(map.space)),
("tile_tints", Value::Array(tints)),
("version", json!(map.version)),
("bounds", rect_json(map.bounds)),
("tile", json!(TILE)),
("geometry", json!({ "card_w": CARD_W, "head_h": HEAD_H, "row_h": ROW_H, "crate_title": CRATE_TITLE, "repo_title": REPO_TITLE })),
("repos", Value::Array(repos)),
("crates", Value::Array(crates)),
("edges_columns", json!(["from", "to", "depends_on", "calls", "call_sites"])),
("edges", Value::Array(edges)),
("tiles", Value::Array(tiles)),
("total_cards", json!(map.cards.len())),
])
}
/// How a call reaches its callee: `same` crate, another `crate` of the same
/// repo, another `repo`, or a `dep`endency.
fn relation(map: &MapLayout, from: u32, to: u32) -> &'static str {
let (a, b) = (&map.crates[from as usize], &map.crates[to as usize]);
if from == to {
"same"
} else if !b.local {
"dep"
} else if a.repo == b.repo {
"crate"
} else {
"repo"
}
}
/// The order a card lists its attributes in.
fn attr_rank(kind: &str) -> u8 {
match kind {
nk::PARAM => 0,
nk::RET => 1,
nk::LOCAL => 2,
_ => 3,
}
}
/// The column a parameter's name stands at in its declaration line — `name` as a
/// whole word followed by `:` (a type, not a `::` path), or a bare `self`; `None`
/// when the line does not show it.
pub fn param_col(line: &str, name: &str) -> Option<u32> {
let word = |c: char| c.is_alphanumeric() || c == '_';
line.match_indices(name).find_map(|(i, _)| {
let (before, after) = (&line[..i], &line[i + name.len()..]);
if before.chars().next_back().is_some_and(word) || after.chars().next().is_some_and(word) {
return None;
}
let rest = after.trim_start();
let typed = rest.starts_with(':') && !rest.starts_with("::");
(typed || name == "self").then_some(i as u32)
})
}
/// **Declaration order of parameters that share a line.** The room keeps a
/// parameter's line, not its column, so two declared on one line came back in
/// the room's own order (`run(p, d)` listed `d` first). This reads those lines
/// from the commit the room was consumed at (the consumer's clone, as the
/// `source` page does) and ranks each parameter by the column its name stands
/// at; an unnamed one (`_0`, `_1`: a pattern) by its index. A line that cannot
/// be read keeps the room's order.
fn param_columns(handle: &CodeRoom, room: &Room, params: &[u64]) -> HashMap<u64, u32> {
let mut texts: HashMap<(String, String), Option<Vec<String>>> = HashMap::new();
let mut out = HashMap::new();
for &o in params {
let name = room.node(o).name.unwrap_or("");
if let Some(i) = name.strip_prefix('_').and_then(|d| d.parse::<u32>().ok()) {
out.insert(o, i);
continue;
}
let Some(sp) = room.span(o) else { continue };
let lines = texts.entry((sp.repo.to_string(), sp.file.to_string())).or_insert_with(|| {
let sha = room.scope().offset_shas.get(sp.repo)?;
let bytes = super::panes::blob_at(&handle.layout().clone_of(sp.repo), sha, sp.file).ok()?;
Some(String::from_utf8_lossy(&bytes).lines().map(str::to_string).collect())
});
let line = lines.as_ref().and_then(|l| l.get((sp.start_line as usize).checked_sub(1)?));
if let Some(c) = line.and_then(|l| param_col(l, name)) {
out.insert(o, c);
}
}
out
}
fn cards(handle: &CodeRoom, room: &Room, map: &MapLayout, tile: (i32, i32)) -> Result<Value, Refusal> {
let ids: &[u32] = map.tiles.get(&tile).map(Vec::as_slice).unwrap_or(&[]);
let nodes: Vec<u64> = ids.iter().map(|i| map.cards[*i as usize].node).collect();
let mask = room.mask_of(&nodes);
let mut rows: HashMap<u64, Vec<(u8, u32, u32, u64, Value)>> = HashMap::new();
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Contains], from: Some(&mask), ..Default::default() }).map_err(internal)?.value.edges {
let v = room.node(e.to);
let Some(k @ (nk::PARAM | nk::RET | nk::LOCAL)) = v.kind else { continue };
let row = json!({ "k": k, "name": v.name, "ty": v.ty, "line": v.start_line });
rows.entry(e.from).or_default().push((attr_rank(k), v.start_line.unwrap_or(0), 0, e.to, row));
}
// parameters sharing a line: their columns (declaration order), read only where needed
let shared: Vec<u64> = rows
.values()
.flat_map(|r| {
let params: Vec<&(u8, u32, u32, u64, Value)> = r.iter().filter(|x| x.0 == attr_rank(nk::PARAM)).collect();
params.iter().filter(|x| params.iter().filter(|y| y.1 == x.1).count() > 1).map(|x| x.3).collect::<Vec<_>>()
})
.collect();
if !shared.is_empty() {
let cols = param_columns(handle, room, &shared);
for x in rows.values_mut().flatten() {
if let Some(c) = cols.get(&x.3) {
x.2 = *c;
}
}
}
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::ReadsField], from: Some(&mask), ..Default::default() }).map_err(internal)?.value.edges {
let v = room.node(e.to);
let owner = v.parent.and_then(|p| room.node(p).name);
let name = match (owner, v.name) {
(Some(o), Some(n)) => format!("{o}.{n}"),
(None, Some(n)) => n.to_string(),
_ => room.moniker(e.to).to_string(),
};
let first = room.sites(e.from, e.to, EdgeKind::ReadsField).iter().map(|s| s.start_line).min();
rows.entry(e.from).or_default().push((3, first.unwrap_or(u32::MAX), 0, e.to, json!({ "k": "field", "name": name, "ty": v.ty, "line": first })));
}
// call sites, one row per callee (its lines bundled), in source order
let mut sites: HashMap<u64, Vec<(u32, Value)>> = HashMap::new();
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&mask), ..Default::default() }).map_err(internal)?.value.edges {
let (Some(&from), Some(&to)) = (map.card_of.get(&e.from), map.card_of.get(&e.to)) else { continue };
let mut lines: Vec<u32> = room.sites(e.from, e.to, EdgeKind::Calls).iter().map(|s| s.start_line).collect();
lines.sort_unstable();
lines.dedup();
let callee = &map.cards[to as usize];
let v = room.node(e.to);
let row = json!({
"to": room.moniker(e.to),
"to_name": v.name,
"to_card": to,
"to_at": map.head_at(to),
"to_y": callee.y,
"to_crate": callee.krate,
"rel": relation(map, map.cards[from as usize].krate, callee.krate),
"lines": lines,
"n": e.count,
});
sites.entry(e.from).or_default().push((lines.first().copied().unwrap_or(u32::MAX), row));
}
let mut out: Vec<Value> = Vec::with_capacity(ids.len());
for &id in ids {
let c = map.cards[id as usize];
let v = room.node(c.node);
let mut r = rows.remove(&c.node).unwrap_or_default();
r.sort_by(|a, b| (a.0, a.1, a.2, a.3).cmp(&(b.0, b.1, b.2, b.3)));
let more_rows = r.len().saturating_sub(ATTR_ROWS_MAX);
let r: Vec<Value> = r.into_iter().take(ATTR_ROWS_MAX).map(|x| x.4).collect();
let mut s = sites.remove(&c.node).unwrap_or_default();
s.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1["to"].as_str().cmp(&b.1["to"].as_str())));
let more_sites = s.len().saturating_sub(SITE_ROWS_MAX);
let s: Vec<Value> = s.into_iter().take(SITE_ROWS_MAX).map(|x| x.1).collect();
out.push(json!({
"card": id,
"y": c.y,
"id": v.moniker,
"path": room.display_path(c.node),
"name": v.name,
"kind": v.kind,
"crate": c.krate,
"home": c.home,
"rect": rect_json(c.rect),
"file": v.file,
"start_line": v.start_line,
"end_line": v.end_line,
"rows": r,
"more_rows": more_rows,
"sites": s,
"more_sites": more_sites,
}));
}
Ok(obj([
("lod", json!("cards")),
("version", json!(map.version)),
("tile", json!([tile.0, tile.1])),
("geometry", json!({ "card_w": CARD_W, "head_h": HEAD_H, "row_h": ROW_H })),
("cards", Value::Array(out)),
]))
}
/// **A plate up close**: its module blocks — each with its name, its cards and the
/// types it defines (struct / enum / union / trait / type alias).
fn modules(room: &Room, map: &MapLayout, krate: u64) -> Result<Value, Refusal> {
let Some(plate) = map.crates.get(krate as usize) else { return Err(invalid(format!("no plate {krate} in this map (version {})", map.version))) };
let blocks: Vec<&ModuleBlock> = map.modules.iter().filter(|m| u64::from(m.krate) == krate).collect();
let nodes: Vec<u64> = blocks.iter().filter_map(|m| m.node).collect();
let mask = room.mask_of(&nodes);
let mut types: HashMap<u64, Vec<(String, &'static str)>> = HashMap::new();
for e in room.edges(&EdgeSel { kinds: vec![EdgeKind::Contains], from: Some(&mask), ..Default::default() }).map_err(internal)?.value.edges {
let v = room.node(e.to);
let k = match v.kind {
Some(nk::STRUCT) => "struct",
Some(nk::ENUM) => "enum",
Some(nk::UNION) => "union",
Some(nk::TRAIT) => "trait",
Some(nk::TYPE) => "type",
_ => continue,
};
types.entry(e.from).or_default().push((v.name.unwrap_or("?").to_string(), k));
}
let out: Vec<Value> = blocks
.iter()
.map(|m| {
let name = match m.node {
Some(n) if room.node(n).level == Some(mimir_schema::Level::Crate) => plate.name.clone(),
Some(n) => room.display_path(n),
None => plate.name.clone(),
};
let mut t = m.node.and_then(|n| types.remove(&n)).unwrap_or_default();
t.sort();
let total = t.len();
json!({
"name": name,
"rect": rect_json(m.rect),
"cards": m.cards,
"types": t.iter().take(MODULE_TYPES_MAX).map(|(n, k)| json!([n, k])).collect::<Vec<_>>(),
"total_types": total,
})
})
.collect();
Ok(obj([
("lod", json!("modules")),
("version", json!(map.version)),
("crate", json!(krate)),
("geometry", json!({ "module_title": MODULE_TITLE, "module_margin": MODULE_MARGIN })),
("modules", Value::Array(out)),
]))
}
/// Callers and callees of a card shown at most per side by `calls`.
const CALLS_MAX: usize = 300;
/// **A head expanded**: every function it calls and every function calling it, each
/// with where its head is (`at`, the top middle of its card) and its call lines.
fn calls(room: &Room, map: &MapLayout, card: u64) -> Result<Value, Refusal> {
let Some(c) = map.cards.get(card as usize).copied() else { return Err(invalid(format!("no card {card} in this map (version {})", map.version))) };
let me = room.mask_of(&[c.node]);
let side = |sel: EdgeSel<'_>, out: bool| -> Result<(Vec<Value>, usize), Refusal> {
let edges = room.edges(&sel).map_err(internal)?.value.edges;
let mut rows: Vec<(u32, Value)> = Vec::new();
for e in &edges {
let other = if out { e.to } else { e.from };
let Some(&oc) = map.card_of.get(&other) else { continue };
let mut lines: Vec<u32> = room.sites(e.from, e.to, EdgeKind::Calls).iter().map(|s| s.start_line).collect();
lines.sort_unstable();
lines.dedup();
let o = &map.cards[oc as usize];
let (a, b) = if out { (c.krate, o.krate) } else { (o.krate, c.krate) };
rows.push((lines.first().copied().unwrap_or(u32::MAX), json!({ "card": oc, "name": room.node(other).name, "path": room.display_path(other), "at": map.head_at(oc), "y": o.y, "rect": rect_json(o.rect), "rel": relation(map, a, b), "lines": lines })));
}
rows.sort_by(|x, y| x.0.cmp(&y.0).then_with(|| x.1["path"].as_str().cmp(&y.1["path"].as_str())));
let total = rows.len();
Ok((rows.into_iter().take(CALLS_MAX).map(|r| r.1).collect(), total))
};
let (callees, n_out) = side(EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&me), ..Default::default() }, true)?;
let (callers, n_in) = side(EdgeSel { kinds: vec![EdgeKind::Calls], to: Some(&me), ..Default::default() }, false)?;
Ok(obj([
("lod", json!("calls")),
("version", json!(map.version)),
("card", json!(card)),
("path", json!(room.display_path(c.node))),
("callees", Value::Array(callees)),
("callers", Value::Array(callers)),
("total_callees", json!(n_out)),
("total_callers", json!(n_in)),
]))
}
fn find(room: &Room, map: &MapLayout, q: &str) -> Result<Value, Refusal> {
if q.is_empty() {
return Err(invalid("`q` is empty: give (part of) a crate or function name"));
}
let want = q.to_lowercase();
// 0 = the name exactly, 1 = it starts with q, 2 = holds it, 3 = the path holds it
let score = |name: &str| {
let n = name.to_lowercase();
if n == want {
Some(0u8)
} else if n.starts_with(&want) {
Some(1)
} else if n.contains(&want) {
Some(2)
} else {
None
}
};
let mut hits: Vec<(u8, u8, String, Value)> = Vec::new();
for (i, c) in map.crates.iter().enumerate() {
if let Some(s) = score(&c.name) {
hits.push((s, 0, c.key.clone(), json!({ "what": "crate", "crate": i, "name": c.name, "path": c.key, "rect": rect_json(c.rect) })));
}
}
let path_q = want.contains("::");
let mut total = hits.len();
for (i, c) in map.cards.iter().enumerate() {
let v = room.node(c.node);
let s = match (score(v.name.unwrap_or("")), path_q) {
(Some(s), _) => Some(s),
(None, true) => room.display_path(c.node).to_lowercase().contains(&want).then_some(3),
(None, false) => None,
};
let Some(s) = s else { continue };
total += 1;
if hits.len() >= FIND_MAX * 8 && s >= hits.iter().map(|h| h.0).max().unwrap_or(0) {
continue;
}
let path = room.display_path(c.node);
hits.push((s, u8::from(!c.home), path.clone(), json!({ "what": "fn", "card": i, "crate": c.krate, "name": v.name, "path": path, "rect": rect_json(c.rect), "home": c.home })));
}
hits.sort_by(|a, b| (a.0, a.1, &a.2).cmp(&(b.0, b.1, &b.2)));
hits.truncate(FIND_MAX);
Ok(obj([("lod", json!("find")), ("q", json!(q)), ("total", json!(total)), ("hits", Value::Array(hits.into_iter().map(|h| h.3).collect()))]))
}
/// One page of a function's source: [`super::panes::source`] of `moniker`,
/// from line `from` (the function's first line when absent), `max_lines`.
fn source_page(reach: &Reach<'_>, p: &Value) -> Result<Value, Refusal> {
let moniker = text(p, "moniker");
if moniker.is_empty() {
return Err(invalid("`moniker` is empty: the source of which function?"));
}
let max = p.get("max_lines").and_then(Value::as_u64).filter(|n| *n > 0).unwrap_or(SOURCE_PAGE);
let mut ask = json!({ "moniker": moniker, "max_lines": max });
if let Some(from) = p.get("from").and_then(Value::as_u64) {
ask["from"] = json!(from);
}
let mut v = super::panes::source(reach, &ask)?;
v["lod"] = json!("source");
Ok(v)
}
/// The lines of one FILE at one commit — the diff hover's before/after side
/// (`repo`, `sha`, `file`, optional `from` line and `max_lines`), read from
/// the consumer's clone.
fn source_at(handle: &CodeRoom, p: &Value) -> Result<Value, Refusal> {
let (repo, sha, file) = (text(p, "repo"), text(p, "sha"), text(p, "file"));
for (name, v) in [("repo", repo), ("sha", sha), ("file", file)] {
if v.is_empty() {
return Err(invalid(format!("`{name}` is empty: lod `source_at` reads one file at one commit")));
}
}
let clone = handle.layout().clone_of(repo);
let mut ev = mimir_room::Evidence { query: format!("source of `{file}` at {sha}"), ..Default::default() };
let from = p.get("from").and_then(Value::as_u64).unwrap_or(1).max(1) as usize;
let max = p.get("max_lines").and_then(Value::as_u64).filter(|n| *n > 0).unwrap_or(SOURCE_PAGE) as usize;
let mut lines: Vec<String> = Vec::new();
let mut file_lines = 0usize;
match super::panes::blob_at(&clone, sha, file) {
Err(e) => miss(&mut ev, "source text", Some(repo), format!("`{file}` at {sha} could not be read from the consumer's clone {}: {e}", clone.display())),
Ok(bytes) => {
let all: Vec<&str> = std::str::from_utf8(&bytes).map(|t| t.lines().collect()).unwrap_or_default();
file_lines = all.len();
let first = from.min(all.len().saturating_add(1));
let stop = all.len().min(first.saturating_sub(1) + max);
if first <= stop {
lines = all[first - 1..stop].iter().map(|l| l.to_string()).collect();
}
}
}
settle(&mut ev, lines.is_empty());
Ok(envelope(
obj([
("lod", json!("source_at")),
("repo", json!(repo)),
("sha", json!(sha)),
("file", json!(file)),
("from", json!(from)),
("file_lines", json!(file_lines)),
("lines", json!(lines)),
]),
&ev,
))
}
/// The consumed commits of one repo, oldest first — the timeline the diff is
/// picked from. Subjects come from the consumer's clone (best effort).
fn commits(handle: &CodeRoom, room: &Room, p: &Value) -> Result<Value, Refusal> {
let repo = text(p, "repo");
let Some(&tip) = room.scope().repos.get(repo) else {
return Err(invalid(format!("`repo` `{repo}` is not in this room: one of {:?}", room.scope().repos.keys().collect::<Vec<_>>())));
};
let log = handle.log()?;
let mut runs = log.block_on(log.runs(repo)).map_err(internal)?;
runs.sort_by_key(|r| r.seq);
let clone = handle.layout().clone_of(repo);
let git = gix::open(&clone).ok();
let subject = |sha: &str| -> Option<String> {
let g = git.as_ref()?;
let id = g.rev_parse_single(sha).ok()?;
let c = id.object().ok()?.try_into_commit().ok()?;
Some(c.message().ok()?.summary().to_string())
};
let list: Vec<Value> = runs
.iter()
.map(|r| {
json!({
"seq": r.seq, "sha": r.sha, "status": r.status, "subject": subject(&r.sha),
"rows_plus": r.rows_plus, "rows_minus": r.rows_minus, "at_tip": r.seq == tip,
})
})
.collect();
let mut ev = mimir_room::Evidence { query: format!("commits of `{repo}`"), ..Default::default() };
note(&mut ev, "the consumed commits, oldest first: pick `from` and `to` seqs for lod `diff`; a commit vs its parent is (seq - 1, seq)");
settle(&mut ev, list.is_empty());
Ok(envelope(obj([("lod", json!("commits")), ("repo", json!(repo)), ("tip", json!(tip)), ("commits", Value::Array(list))]), &ev))
}
/// One text cell of a diff row, cast to text whatever the column's type.
fn cell(b: &skade::arrow_array::RecordBatch, name: &str, i: usize) -> Option<String> {
use skade::arrow_array::Array;
let c = b.column_by_name(name)?;
let c = skade::arrow_cast::cast(c, &skade::arrow_schema::DataType::Utf8).ok()?;
let c = c.as_any().downcast_ref::<skade::arrow_array::StringArray>()?;
(!c.is_null(i)).then(|| c.value(i).to_string())
}
/// One head of the diff: what changed, and where it is on the map.
struct DiffHead {
class: &'static str,
moniker: String,
name: Option<String>,
kind: String,
crate_key: String,
file: Option<String>,
line: Option<u64>,
end_line: Option<u64>,
/// The BEFORE side of a changed head (its from-cover row).
before: Option<Value>,
/// The current map's card, when the head is on it.
card: Option<u32>,
/// Where to draw it: the card's head, or a ghost spot below its plate.
at: Option<[f32; 2]>,
/// The node's payload moved (Room::diff's `changed`); a body-only
/// rewrite has `false` here and `body_changed` true.
payload_changed: bool,
/// The body's content hash moved in the links log.
body_changed: bool,
/// The body hash at each end (the links log), for the jump.
link_from: Option<String>,
link_to: Option<String>,
/// The MEANING JUMP: 1 − cosine(old body vector, new body vector); 0 for
/// an unchanged body; `None` when a side has no vector yet.
jump: Option<f32>,
jump_missing: Option<&'static str>,
}
/// What changed in `repo` between two consumed seqs, placed on THIS map:
/// added / removed / changed fn heads (removed ones get ghost spots below
/// their crate's plate), the CALLS splines among them, and per-plate change
/// counts — answered from the READ-FAST zone's interval tables
/// ([`mimir_ingest::EventLog::interval_diff`]), never two room builds.
fn diff(reach: &Reach<'_>, room: &Room, map: &MapLayout, p: &Value) -> Result<Value, Refusal> {
use mimir_ingest::Kind;
let handle = reach.code_room()?;
let repo = text(p, "repo");
let Some(&tip) = room.scope().repos.get(repo) else {
return Err(invalid(format!("`repo` `{repo}` is not in this room: one of {:?}", room.scope().repos.keys().collect::<Vec<_>>())));
};
let Some(from) = p.get("from").and_then(Value::as_i64) else {
return Err(invalid("`from` (a consumer seq, see lod `commits`) for lod `diff`"));
};
let to = p.get("to").and_then(Value::as_i64).unwrap_or(tip);
if from >= to || from < 0 {
return Err(invalid(format!("an empty window: `from` {from} is not before `to` {to}")));
}
let log = handle.log()?;
let nodes_d = log.block_on(log.interval_diff(Kind::Nodes, repo, from, to)).map_err(internal)?;
let edges_d = log.block_on(log.interval_diff(Kind::Edges, repo, from, to)).map_err(internal)?;
let spans_d = log.block_on(log.interval_diff(Kind::Spans, repo, from, to)).map_err(internal)?;
let runs = log.block_on(log.runs(repo)).map_err(internal)?;
let sha_of = |seq: i64| runs.iter().find(|r| r.seq == seq).map(|r| r.sha.clone());
// where a node STOOD at `from`: its removed span row (positions live in
// the span events, not on the node) — the before side of the hover
let mut stood: HashMap<String, (String, u64, u64)> = HashMap::new();
for &(bi, ri) in &spans_d.removed {
let b = &spans_d.batches[bi as usize];
let i = ri as usize;
if cell(b, "target", i).as_deref() != Some(mimir_schema::SPAN_NODE) {
continue;
}
if let (Some(key), Some(file), Some(line)) = (cell(b, "key", i), cell(b, "file", i), cell(b, "start_line", i).and_then(|v| v.parse::<u64>().ok())) {
let end = cell(b, "end_line", i).and_then(|v| v.parse::<u64>().ok()).unwrap_or(line);
stood.entry(key).or_insert((file, line, end));
}
}
// ---- the heads: every added / removed / changed local fn or method,
// ONE per identity (a unit dumped under its lib and its test harness
// names the same head twice)
let mut seen_heads: std::collections::BTreeSet<(&'static str, String)> = std::collections::BTreeSet::new();
let mut heads: Vec<DiffHead> = Vec::new();
let classes: [(&'static str, &[(u32, u32)], Option<&[(u32, u32)]>); 3] = [
("added", &nodes_d.added, None),
("removed", &nodes_d.removed, None),
("changed", &nodes_d.changed, Some(&nodes_d.changed_from)),
];
for (class, refs, _befores) in classes {
for &(bi, ri) in refs.iter() {
let b = &nodes_d.batches[bi as usize];
let i = ri as usize;
let kind = cell(b, "kind", i).unwrap_or_default();
if kind != nk::FN && kind != nk::METHOD {
continue;
}
if cell(b, "origin", i).as_deref() != Some("local") {
continue;
}
let moniker = cell(b, "id", i).unwrap_or_default();
if !seen_heads.insert((class, moniker.clone())) {
continue;
}
let crate_key = crate_key(&cell(b, "crate", i).unwrap_or_default(), cell(b, "version", i).as_deref());
// where it stood at `from` (removed and changed heads)
let before = (class != "added")
.then(|| stood.get(&moniker))
.flatten()
.map(|(file, line, end)| json!({ "file": file, "line": line, "end_line": end }));
let card = room.ordinal(&moniker).and_then(|o| map.card_of.get(&o)).copied();
// where it stands now: the room's span (the current side)
let now = room.ordinal(&moniker).and_then(|o| room.span(o));
let (file, line, end_line) = match (&now, stood.get(&moniker)) {
(Some(sp), _) => (Some(sp.file.to_string()), Some(sp.start_line as u64), Some(sp.end_line as u64)),
(None, Some((f, l, e))) => (Some(f.clone()), Some(*l), Some(*e)),
(None, None) => (None, None, None),
};
heads.push(DiffHead {
class,
name: cell(b, "name", i),
kind,
crate_key,
file,
line,
end_line,
before,
card,
at: card.map(|c| map.head_at(c)),
moniker,
payload_changed: class == "changed",
body_changed: false,
link_from: None,
link_to: None,
jump: None,
jump_missing: None,
});
}
}
// ---- the MEANING JUMP (owner 2026-10-17): the body's content hash at
// each end comes from the embeddings' link log, so a body-only rewrite
// (same signature — invisible to the node payload) JOINS the changed set
// here; the jump itself is 1 − cosine of the two body vectors.
let mut jump_note: Option<String> = None;
if let Some(wh) = reach.warehouse {
use crate::vector::fnvec::links;
let events = links::read_events_of(wh, &[repo]).map_err(internal)?;
let state_from = links::open(&events, repo, from);
let state_to = links::open(&events, repo, to);
let known: std::collections::BTreeSet<String> = heads.iter().map(|h| h.moniker.clone()).collect();
for (m, h_to) in &state_to {
let Some(h_from) = state_from.get(m) else { continue };
if h_from == h_to || known.contains(m) {
continue;
}
let Some(o) = room.ordinal(m) else { continue };
let v = room.node(o);
if v.repo != Some(repo) || !matches!(v.kind, Some(nk::FN | nk::METHOD)) {
continue;
}
let card = map.card_of.get(&o).copied();
let sp = room.span(o);
heads.push(DiffHead {
class: "changed",
moniker: m.clone(),
name: v.name.map(str::to_string),
kind: v.kind.unwrap_or(nk::FN).to_string(),
crate_key: crate_key(v.krate.unwrap_or(""), v.version),
file: sp.as_ref().map(|x| x.file.to_string()),
line: sp.as_ref().map(|x| x.start_line as u64),
end_line: sp.as_ref().map(|x| x.end_line as u64),
before: stood.get(m).map(|(f, l, e)| json!({ "file": f, "line": l, "end_line": e })),
card,
at: card.map(|c| map.head_at(c)),
payload_changed: false,
body_changed: true,
link_from: None,
link_to: None,
jump: None,
jump_missing: None,
});
}
// the hashes at each end, per changed head
let mut needed: std::collections::HashSet<String> = std::collections::HashSet::new();
for h in heads.iter_mut().filter(|h| h.class == "changed") {
h.link_from = state_from.get(&h.moniker).cloned();
h.link_to = state_to.get(&h.moniker).cloned();
if let (Some(a), Some(b)) = (&h.link_from, &h.link_to) {
h.body_changed = a != b;
if a != b {
needed.insert(a.clone());
needed.insert(b.clone());
}
}
}
// the model whose vectors the lake holds: the latest run that wrote
let profile = links::read_runs(wh)
.map_err(internal)?
.into_iter()
.filter(|r| r.status == "ok")
.max_by_key(|r| r.ts_micros)
.map(|r| r.model_profile);
let vectors = match (&profile, needed.is_empty()) {
(Some(prof), false) => {
let need: std::collections::HashSet<&str> = needed.iter().map(String::as_str).collect();
wh.block_on(crate::vector::store::read_vectors_present(wh, prof, &need)).map_err(internal)?.0
}
_ => Default::default(),
};
if profile.is_none() {
jump_note = Some("no embedding run has written vectors yet: every jump is unmeasured".to_string());
}
let cos = |a: &[f32], b: &[f32]| -> f32 {
let dot: f32 = a.iter().zip(b).map(|(x, y)| x * y).sum();
let (na, nb) = (a.iter().map(|x| x * x).sum::<f32>().sqrt(), b.iter().map(|x| x * x).sum::<f32>().sqrt());
if na == 0.0 || nb == 0.0 { 0.0 } else { dot / (na * nb) }
};
for h in heads.iter_mut().filter(|h| h.class == "changed") {
match (&h.link_from, &h.link_to) {
(Some(a), Some(b)) if a == b => h.jump = Some(0.0),
(Some(a), Some(b)) => match (vectors.get(a), vectors.get(b)) {
(Some(va), Some(vb)) => h.jump = Some((1.0 - cos(va, vb)).max(0.0)),
(None, Some(_)) => h.jump_missing = Some("old"),
(Some(_), None) => h.jump_missing = Some("new"),
(None, None) => h.jump_missing = Some("both"),
},
_ => h.jump_missing = Some("both"),
}
}
} else {
jump_note = Some("no warehouse in reach of this door: body-only changes and meaning jumps are not measured".to_string());
}
// ---- ghost spots: a head with no card (removed, or not at this room's
// seq) lies in a strip below its crate's plate, in (file, line) order; a
// crate with no plate gets a strip at the map's south edge.
let mut by_plate: BTreeMap<Option<usize>, Vec<usize>> = BTreeMap::new();
for (i, h) in heads.iter().enumerate() {
if h.at.is_none() {
let plate = map.crates.iter().position(|c| c.key == h.crate_key);
by_plate.entry(plate).or_default().push(i);
}
}
let mut south_x = map.bounds[0];
for (plate, mut idxs) in by_plate {
idxs.sort_by(|&a, &b| (heads[a].file.as_deref(), heads[a].line).cmp(&(heads[b].file.as_deref(), heads[b].line)));
let step = CARD_W + GAP_X;
let (x0, z0, width) = match plate {
Some(pi) => {
let r = map.crates[pi].rect;
(r[0], r[3] + GAP_Z, (r[2] - r[0]).max(step))
}
None => {
let x = south_x;
south_x += step * 8.0 + REPO_MARGIN;
(x, map.bounds[3] + REPO_MARGIN, step * 8.0)
}
};
let per_row = ((width / step).floor() as usize).max(1);
for (n, &i) in idxs.iter().enumerate() {
let (ix, iz) = (n % per_row, n / per_row);
heads[i].at = Some([x0 + ix as f32 * step + CARD_W * 0.5, z0 + iz as f32 * (HEAD_H + GAP_Z)]);
}
}
let ghost_at: HashMap<&str, [f32; 2]> = heads.iter().filter(|h| h.card.is_none()).filter_map(|h| h.at.map(|a| (h.moniker.as_str(), a))).collect();
// ---- the CALLS splines among the changed
let place = |moniker: &str| -> Option<[f32; 2]> {
room.ordinal(moniker)
.and_then(|o| map.card_of.get(&o))
.map(|&c| map.head_at(c))
.or_else(|| ghost_at.get(moniker).copied())
};
let mut splines: Vec<Value> = Vec::new();
let mut seen_splines: std::collections::BTreeSet<(&'static str, String, String)> = std::collections::BTreeSet::new();
let edge_classes: [(&'static str, &[(u32, u32)]); 3] = [("added", &edges_d.added), ("removed", &edges_d.removed), ("changed", &edges_d.changed)];
for (class, refs) in edge_classes {
for &(bi, ri) in refs {
let b = &edges_d.batches[bi as usize];
let i = ri as usize;
if cell(b, "kind", i).as_deref() != Some(EdgeKind::Calls.as_str()) {
continue;
}
let (f, t) = (cell(b, "from", i).unwrap_or_default(), cell(b, "to", i).unwrap_or_default());
if !seen_splines.insert((class, f.clone(), t.clone())) {
continue;
}
splines.push(json!({
"class": class,
"from": f, "to": t,
"from_at": place(&f), "to_at": place(&t),
}));
}
}
// ---- per-plate counts (and summed jump), readable from far out
let mut counts: BTreeMap<&str, (u64, u64, u64, f64)> = BTreeMap::new();
for h in &heads {
let c = counts.entry(h.crate_key.as_str()).or_default();
match h.class {
"added" => c.0 += 1,
"removed" => c.1 += 1,
_ => c.2 += 1,
}
c.3 += h.jump.unwrap_or(0.0) as f64;
}
let plates: Vec<Value> = counts
.into_iter()
.map(|(key, (a, r, ch, j))| {
let plate = map.crates.iter().position(|c| c.key == key);
json!({ "key": key, "crate": plate, "rect": plate.map(|p| rect_json(map.crates[p].rect)), "added": a, "removed": r, "changed": ch, "jump": j })
})
.collect();
// the biggest meaning changes of the window, measured ones first
let mut top: Vec<&DiffHead> = heads.iter().filter(|h| h.jump.is_some_and(|j| j > 0.0)).collect();
top.sort_by(|x, y| y.jump.partial_cmp(&x.jump).unwrap_or(std::cmp::Ordering::Equal));
let top_jumps: Vec<Value> = top
.iter()
.take(12)
.map(|h| json!({ "moniker": h.moniker, "name": h.name, "jump": h.jump, "card": h.card, "at": h.at }))
.collect();
let changed_n = heads.iter().filter(|h| h.class == "changed").count() as u64;
let measured_n = heads.iter().filter(|h| h.class == "changed" && h.jump.is_some()).count() as u64;
let missing_n = changed_n - measured_n;
if jump_note.is_none() && missing_n > 0 {
jump_note = Some(format!(
"{missing_n} change(s) have no vector yet — the embeddings feed embeds new bodies after each consume, and the next ask measures them"
));
}
let nodes: Vec<Value> = heads
.iter()
.map(|h| {
json!({
"class": h.class, "moniker": h.moniker, "name": h.name, "kind": h.kind, "crate": h.crate_key,
"file": h.file, "line": h.line, "end_line": h.end_line,
"card": h.card, "at": h.at, "ghost": h.card.is_none(), "before": h.before,
"payload_changed": h.payload_changed, "body_changed": h.body_changed,
"jump": h.jump, "jump_missing": h.jump_missing,
})
})
.collect();
Ok(obj([
("lod", json!("diff")),
("repo", json!(repo)),
("from", json!(from)),
("to", json!(to)),
("from_sha", json!(sha_of(from))),
("to_sha", json!(sha_of(to))),
("nodes", Value::Array(nodes)),
("edges", Value::Array(splines)),
("plates", Value::Array(plates)),
("top_jumps", Value::Array(top_jumps)),
("jump_coverage", json!({ "changed": changed_n, "measured": measured_n, "missing": missing_n, "note": jump_note })),
("events_read", json!(nodes_d.events_read + edges_d.events_read)),
]))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::code_room::reader::fixture;
fn ask(ws: &fixture::Workspace, p: Value) -> Value {
super::super::answer("Viz.CodeMap", &Reach::room_only(&ws.handle), &p).unwrap_or_else(|r| panic!("refused [{}] {}", r.state, r.note))
}
fn rect(v: &Value) -> Rect {
let a = v.as_array().unwrap_or_else(|| panic!("not a rect: {v}"));
[0, 1, 2, 3].map(|i| a[i].as_f64().unwrap() as f32)
}
fn apart(a: Rect, b: Rect) -> bool {
a[2] <= b[0] || b[2] <= a[0] || a[3] <= b[1] || b[3] <= a[1]
}
/// **The map of a REAL small crate** (the driver's fixture, dumped by the real
/// driver): `b::run` is a card whose head carries its name, with its params,
/// its return type, its locals and the fields it reads under it, and its call
/// sites in source order — two calls to `a::add` bundled on one row with both
/// lines, its spline landing on the TOP of `add`'s card; a dependency's
/// function (`a 0.0.9`) a card on a plate of the dependencies.
#[test]
fn the_map_of_a_real_crate_has_heads_with_params_locals_and_call_sites() {
let ws = fixture::real();
let w = ask(ws, json!({ "lod": "world" }));
assert_eq!(w["state"], "complete", "{}", w["evidence"]);
let crates = w["crates"].as_array().unwrap();
let b = crates.iter().position(|c| c["id"] == "b@0.2.0").unwrap_or_else(|| panic!("no plate b@0.2.0: {crates:?}"));
assert_eq!((crates[b]["local"].as_bool(), crates[b]["repo"].as_str()), (Some(true), Some("fixture")));
let old = crates.iter().find(|c| c["id"] == "a@0.0.9").expect("the dependency a 0.0.9 has a plate");
assert_eq!(old["local"], false);
for (i, x) in crates.iter().enumerate() {
for y in &crates[i + 1..] {
assert!(apart(rect(&x["rect"]), rect(&y["rect"])), "plates {} and {} overlap", x["id"], y["id"]);
}
}
assert!(rect(&old["rect"])[0] > rect(&crates[b]["rect"])[2], "the dependencies stand further out (to the right)");
assert!(w["edges"].as_array().unwrap().iter().any(|e| e[0] == json!(b) && e[2].as_u64() > Some(0)), "b's DEPENDS_ON edges are bundled: {}", w["edges"]);
let found = ask(ws, json!({ "lod": "find", "q": "run" }));
let hit = found["hits"].as_array().unwrap().iter().find(|h| h["what"] == "fn" && h["crate"] == json!(b)).unwrap_or_else(|| panic!("no fn run of b: {found}"));
let r = rect(&hit["rect"]);
let (tx, tz) = tile_of((r[0] + r[2]) * 0.5, (r[1] + r[3]) * 0.5);
let t = ask(ws, json!({ "lod": "cards", "tile": [tx, tz] }));
let run = t["cards"].as_array().unwrap().iter().find(|c| c["card"] == hit["card"]).unwrap_or_else(|| panic!("run is not in its tile: {t}"));
assert_eq!((run["name"].as_str(), run["file"].as_str(), run["start_line"].as_u64()), (Some("run"), Some("b/src/lib.rs"), Some(17)), "{run}");
assert_eq!(rect(&run["rect"]), r);
let rows = run["rows"].as_array().unwrap();
let row = |k: &str, name: &str| rows.iter().find(|x| x["k"] == k && x["name"] == name).cloned().unwrap_or_else(|| panic!("no {k} {name} under run: {rows:?}"));
// params first, in DECLARATION order (`run(p: &Point, d: &Dog)` — both on one
// line, so the column decides, not the room's order), then the return type,
// then the locals by line
let kinds: Vec<&str> = rows.iter().map(|x| x["k"].as_str().unwrap()).collect();
assert_eq!(&kinds[..3], &["param", "param", "ret"], "{rows:?}");
let params: Vec<&str> = rows.iter().filter(|x| x["k"] == "param").map(|x| x["name"].as_str().unwrap()).collect();
assert_eq!(params, ["p", "d"], "declaration order: {rows:?}");
row("param", "p");
assert!(row("param", "d")["ty"].as_str().unwrap().contains("Dog"));
assert_eq!(row("ret", "->")["ty"], "i32");
assert_eq!((row("local", "s")["ty"].as_str(), row("local", "s")["line"].as_u64()), (Some("String"), Some(18)));
assert_eq!(rows.iter().filter(|x| x["k"] == "local" && x["name"] == "v").count(), 2, "a shadowed `v` is two locals");
assert_eq!(rows.len(), ATTR_ROWS_MAX, "capped");
assert!(run["more_rows"].as_u64().unwrap() > 0, "and the rest counted: {run}");
let sites = run["sites"].as_array().unwrap();
let firsts: Vec<u64> = sites.iter().map(|s| s["lines"][0].as_u64().unwrap_or(u64::MAX)).collect();
assert!(firsts.windows(2).all(|w| w[0] <= w[1]), "call sites in source order: {firsts:?}");
let add = sites.iter().find(|s| s["to_name"] == "add" && s["rel"] == "crate").unwrap_or_else(|| panic!("no call to a::add: {sites:?}"));
assert_eq!(add["lines"], json!([21, 22]), "two calls to one callee bundle on one row");
let old_add = sites.iter().find(|s| s["to_name"] == "add" && s["rel"] == "dep").unwrap_or_else(|| panic!("no call to a 0.0.9's add: {sites:?}"));
assert_eq!(old_add["lines"], json!([23]));
// the spline lands on the top middle of the callee's card
let callee = ask(ws, json!({ "lod": "find", "q": "add" }));
let card = callee["hits"].as_array().unwrap().iter().find(|h| h["card"] == add["to_card"]).unwrap_or_else(|| panic!("{callee}"));
let cr = rect(&card["rect"]);
assert_eq!(add["to_at"], json!([(cr[0] + cr[2]) * 0.5, cr[1]]));
// expanded: `add` (a 0.1.0) is called by run, at 21 and 22
let x = ask(ws, json!({ "lod": "calls", "card": add["to_card"] }));
let by_run = x["callers"].as_array().unwrap().iter().find(|c| c["card"] == hit["card"]).unwrap_or_else(|| panic!("run is not among add's callers: {x}"));
assert_eq!((by_run["lines"].clone(), by_run["rel"].as_str()), (json!([21, 22]), Some("crate")));
assert_eq!(by_run["at"], json!([(r[0] + r[2]) * 0.5, r[1]]), "with where run's head is");
}
/// **A plate up close is its modules**: blocks that do not overlap, inside their
/// plate, every card inside its module's block; each named, with the types it
/// defines (crate `a`: its root holds `Point`, `Greet`; `inner` is its own block).
#[test]
fn a_plate_up_close_is_its_modules_with_their_types() {
let ws = fixture::real();
let w = ask(ws, json!({ "lod": "world" }));
let crates = w["crates"].as_array().unwrap();
let a = crates.iter().position(|c| c["id"] == "a@0.1.0").unwrap_or_else(|| panic!("no plate a@0.1.0: {crates:?}"));
let m = ask(ws, json!({ "lod": "modules", "crate": a }));
let blocks = m["modules"].as_array().unwrap();
assert!(blocks.len() >= 2, "{m}");
assert_eq!(crates[a]["modules"].as_u64(), Some(blocks.len() as u64));
let plate = rect(&crates[a]["rect"]);
for (i, x) in blocks.iter().enumerate() {
let r = rect(&x["rect"]);
assert!(r[0] >= plate[0] && r[1] >= plate[1] && r[2] <= plate[2] && r[3] <= plate[3], "{} lies on its plate", x["name"]);
for y in &blocks[i + 1..] {
assert!(apart(r, rect(&y["rect"])), "modules {} and {} overlap", x["name"], y["name"]);
}
}
let root = blocks.iter().find(|b| b["name"] == "a").unwrap_or_else(|| panic!("no root block: {m}"));
let types: Vec<&str> = root["types"].as_array().unwrap().iter().map(|t| t[0].as_str().unwrap()).collect();
assert!(types.contains(&"Point") && types.contains(&"Greet"), "{types:?}");
assert!(blocks.iter().any(|b| b["name"].as_str().is_some_and(|n| n.ends_with("inner"))), "{m}");
// every card of the plate lies inside one module block
let layout = MapLayout::of(&ws.handle.room(&mimir_room::Scope::current()).unwrap()).unwrap();
for c in layout.cards.iter().filter(|c| c.krate as usize == a) {
assert!(layout.modules.iter().filter(|m| m.krate as usize == a).any(|m| c.rect[0] >= m.rect[0] && c.rect[2] <= m.rect[2] && c.rect[1] >= m.rect[1] && c.rect[3] <= m.rect[3]), "card {:?} is in no module block", c.rect);
}
}
/// **The map by role** of a real crate: every function of the workspace once,
/// no two cards on one cell, the crates a tint (tiles say their main crate);
/// a card's tile answer carries its height.
#[test]
fn the_map_by_role_places_every_function_once() {
let ws = fixture::real();
let w = ask(ws, json!({ "lod": "world", "layout": "role" }));
assert_eq!((w["layout"].as_str(), w["state"].as_str()), (Some("role"), Some("complete")), "{}", w["evidence"]);
assert!(w["space"].as_str().is_some_and(|s| !s.is_empty()));
let crate_mode = ask(ws, json!({ "lod": "world" }));
assert_eq!(w["total_cards"], crate_mode["total_cards"], "the same cards");
assert!(!w["tile_tints"].as_array().unwrap().is_empty());
let mut rects: Vec<Rect> = Vec::new();
for t in w["tiles"].as_array().unwrap() {
let c = ask(ws, json!({ "lod": "cards", "layout": "role", "tile": [t[0], t[1]] }));
for card in c["cards"].as_array().unwrap() {
assert!(card["y"].as_f64().is_some_and(|y| y.abs() <= f64::from(ROLE_Y) + 1e-3), "{card}");
rects.push(rect(&card["rect"]));
}
}
assert_eq!(rects.len() as u64, w["total_cards"].as_u64().unwrap());
for (i, a) in rects.iter().enumerate() {
for b in &rects[i + 1..] {
assert!(apart(*a, *b), "{a:?} {b:?}");
}
}
}
/// **The source of a function, as the commit has it**, a page at a time.
#[test]
fn the_source_of_a_head_is_the_commits_text_paged_by_lines() {
let ws = fixture::real();
let moniker = "rust-analyzer cargo b 0.2.0 run().";
let v = ask(ws, json!({ "lod": "source", "moniker": moniker }));
let sha = v["sha"].as_str().unwrap_or_else(|| panic!("no sha: {v}"));
let src = ws.handle.layout().base().join("src-fixture");
let out = std::process::Command::new("git").arg("-C").arg(&src).args(["show", &format!("{sha}:b/src/lib.rs")]).output().unwrap();
assert!(out.status.success());
let file: Vec<String> = String::from_utf8(out.stdout).unwrap().lines().map(str::to_string).collect();
let (start, end) = (v["start_line"].as_u64().unwrap() as usize, v["end_line"].as_u64().unwrap() as usize);
assert_eq!(start, 17);
assert_eq!(v["lines"], json!(file[start - 1..end]), "the lines of the commit the room was read at");
assert!(v["lines"][0].as_str().unwrap().contains("pub fn run("));
let page = ask(ws, json!({ "lod": "source", "moniker": moniker, "from": 21, "max_lines": 2 }));
assert_eq!(page["lines"], json!(file[20..22]), "a page further in");
assert_eq!((page["from"].as_u64(), page["truncated"].as_bool()), (Some(21), Some(true)));
}
/// Cards of one column never overlap, and a column is no taller than the block is wide (± one card).
/// A parameter's column is where its NAME is declared — a whole word typed by
/// `:` (or a bare `self`), not the fn's own name, a type, or a `::` path.
#[test]
fn a_param_column_is_where_its_name_is_declared() {
let line = "pub fn p(&self, d: &Dog, p: p::P, mut pd: u8) -> i32 {";
let col = |n: &str| param_col(line, n).map(|c| c as usize);
assert_eq!(col("self"), line.find("self"));
assert_eq!(col("d"), line.find("d:"));
assert_eq!(col("p"), line.find("p: p::P"), "not the fn `p`, not the path `p::P`");
assert_eq!(col("pd"), line.find("pd:"));
assert!(col("d") < col("p") && col("p") < col("pd"));
assert_eq!(param_col(line, "q"), None);
}
#[test]
fn packed_cards_never_overlap() {
let heights: Vec<f32> = (0..57).map(|i| card_height(i % 13, i % 7)).collect();
let (at, (w, d)) = pack_columns(&heights);
let rects: Vec<Rect> = at.iter().zip(&heights).map(|((x, z), h)| [*x, *z, x + CARD_W, z + h]).collect();
for (i, a) in rects.iter().enumerate() {
assert!(a[2] <= w + 1e-3 && a[3] <= d + 1e-3, "inside the block");
for b in &rects[i + 1..] {
let apart = a[2] + GAP_X - 1e-3 <= b[0] || b[2] + GAP_X - 1e-3 <= a[0] || a[3] + GAP_Z - 1e-3 <= b[1] || b[3] + GAP_Z - 1e-3 <= a[1];
assert!(apart, "{a:?} and {b:?} overlap (or touch)");
}
}
}
/// A map of two crates far apart, three cards each, laid out by crate.
fn two_crates() -> MapLayout {
let plate = |key: &str, x: f32| CratePlate { key: key.into(), name: key.into(), version: "0.1.0".into(), repo: Some("r".into()), local: true, rect: [x, 0.0, x + 30.0, 40.0], title: CRATE_TITLE, cards: 3 };
let mut m = MapLayout { version: 1, layout: "crate", crates: vec![plate("a", 0.0), plate("b", 500.0)], ..Default::default() };
for i in 0..6u64 {
let (k, x) = if i < 3 { (0u32, 3.0) } else { (1u32, 503.0) };
let z = 10.0 + (i % 3) as f32 * 8.0;
m.card_of.insert(100 + i, m.cards.len() as u32);
m.cards.push(Card { node: 100 + i, krate: k, rect: [x, z, x + CARD_W, z + 4.0], home: true, y: 0.0 });
}
m
}
fn centre(c: &Card) -> (f32, f32) {
((c.rect[0] + c.rect[2]) * 0.5, (c.rect[1] + c.rect[3]) * 0.5)
}
fn dist(a: &Card, b: &Card) -> f32 {
let (p, q) = (centre(a), centre(b));
((p.0 - q.0).powi(2) + (p.1 - q.1).powi(2) + (a.y - b.y).powi(2)).sqrt()
}
/// **By role, two functions that do the same thing stand together, across
/// crates; by crate they stand apart.** Cards 1 (crate a) and 4 (crate b) have
/// the same role vector; every other card a different one.
#[test]
fn same_role_functions_in_two_crates_stand_together_by_role_and_apart_by_crate() {
let base = two_crates();
let vecs: Vec<Vec<f32>> = vec![
vec![1.0, 0.0, 0.0, 0.2],
vec![0.0, 1.0, 0.0, 0.0],
vec![0.0, 0.0, 1.0, 0.1],
vec![-1.0, 0.3, 0.0, 0.0],
vec![0.0, 1.0, 0.0, 0.0],
vec![0.2, -1.0, 0.5, 0.0],
];
let coords: HashMap<u64, [f32; 3]> = (0..6u64).map(|i| 100 + i).zip(pca3(&vecs)).collect();
let role = base.by_role(&coords, "test");
assert_eq!(role.layout, "role");
assert_eq!(role.cards.len(), 6, "the same cards, the same ids");
let cell = CARD_W + GAP_X;
let together = dist(&role.cards[1], &role.cards[4]);
let apart_by_crate = dist(&base.cards[1], &base.cards[4]);
assert!(together <= 1.5 * cell.max(card_height(ATTR_ROWS_MAX + 1, SITE_ROWS_MAX + 1) + GAP_Z), "by role, side by side: {together}");
assert!(apart_by_crate > 400.0, "by crate, apart: {apart_by_crate}");
// and no two cards on one cell, no plate drawn
for (i, a) in role.cards.iter().enumerate() {
for b in &role.cards[i + 1..] {
assert!(apart(a.rect, b.rect), "{:?} {:?}", a.rect, b.rect);
}
}
assert!(role.crates.iter().all(|c| c.rect == [0.0; 4]), "crates are a tint by role, not plates");
// the same coordinates give the same map
assert_eq!(base.by_role(&coords, "test").cards.iter().map(|c| c.rect).collect::<Vec<_>>(), role.cards.iter().map(|c| c.rect).collect::<Vec<_>>());
}
/// PCA finds the axis of the spread first, and is stable in sign.
#[test]
fn pca_puts_the_widest_spread_on_the_first_axis() {
let rows: Vec<Vec<f32>> = (0..20).map(|i| vec![i as f32, (i % 2) as f32 * 0.1, 0.0]).collect();
let p = pca3(&rows);
assert!(p[19][0] - p[0][0] > 15.0, "{:?} {:?}", p[0], p[19]);
assert!(p.iter().all(|x| x[1].abs() < 0.2), "the small spread on the second axis");
}
#[test]
fn shelves_never_overlap() {
let sizes: Vec<(f32, f32)> = (0..23).map(|i| (10.0 + (i * 7 % 30) as f32, 8.0 + (i * 11 % 25) as f32)).collect();
let (at, _) = pack_shelves(&sizes, 2.0);
for i in 0..sizes.len() {
for j in i + 1..sizes.len() {
let (a, b) = ((at[i], sizes[i]), (at[j], sizes[j]));
let apart = a.0.0 + a.1.0 <= b.0.0 || b.0.0 + b.1.0 <= a.0.0 || a.0.1 + a.1.1 <= b.0.1 || b.0.1 + b.1.1 <= a.0.1;
assert!(apart, "{i} {j}");
}
}
}
/// **The commit diff of a REAL small crate on the map.** Two consumed
/// commits (commit 2 removes `a::dead`, documents `a::norm1`, adds
/// `a::born` calling `a::tiny`): the timeline lists both; the diff
/// answers from the interval tables and equals `Room::diff` of the two
/// as-of rooms (the fold diff) on the fn heads; the removed head gets a
/// ghost spot, the added CALLS spline lands on both heads; and the
/// before/after hover reads both commits\' source.
#[test]
fn the_commit_diff_of_a_real_crate_is_the_fold_diff_tinted_on_the_map() {
let ws = fixture::real_diff();
// the timeline
let c = ask(ws, json!({ "lod": "commits", "repo": "fixture" }));
let commits = c["commits"].as_array().unwrap();
assert_eq!((commits.len(), c["tip"].as_i64()), (2, Some(2)), "{c}");
assert!(commits[1]["subject"].as_str().unwrap_or_default().contains("dead goes"), "{c}");
// the diff, placed on the map
let d = ask(ws, json!({ "lod": "diff", "repo": "fixture", "from": 1 }));
assert_eq!((d["from"].as_i64(), d["to"].as_i64()), (Some(1), Some(2)), "{d}");
assert!(d["from_sha"].as_str().is_some_and(|s| !s.is_empty()) && d["to_sha"].as_str().is_some_and(|s| !s.is_empty()), "{d}");
let nodes = d["nodes"].as_array().unwrap();
let of = |class: &str| nodes.iter().filter(|n| n["class"] == class).map(|n| n["name"].as_str().unwrap_or_default().to_string()).collect::<Vec<_>>();
assert_eq!(of("added"), ["born"], "{d}");
assert_eq!(of("removed"), ["dead"], "{d}");
assert_eq!(of("changed"), ["norm1"], "{d}");
for n in nodes {
assert!(n["at"].as_array().is_some_and(|a| a.len() == 2), "every head has a place: {n}");
}
let dead = nodes.iter().find(|n| n["class"] == "removed").unwrap();
assert_eq!(dead["ghost"], true, "a removed head is a ghost: {dead}");
let born = nodes.iter().find(|n| n["class"] == "added").unwrap();
assert_eq!(born["ghost"], false, "the added head is on its card: {born}");
let norm1 = nodes.iter().find(|n| n["class"] == "changed").unwrap();
assert!(norm1["before"]["line"].as_u64().is_some(), "a changed head carries its before side: {norm1}");
// the added CALLS spline, both ends placed
let edges = d["edges"].as_array().unwrap();
let call = edges.iter().find(|e| e["class"] == "added" && e["from"].as_str().unwrap_or_default().contains("born")).unwrap_or_else(|| panic!("{edges:?}"));
assert!(call["from_at"].as_array().is_some() && call["to_at"].as_array().is_some(), "{call}");
// the plate counts
let plates = d["plates"].as_array().unwrap();
let a = plates.iter().find(|p| p["key"].as_str().unwrap_or_default().starts_with("a@")).unwrap_or_else(|| panic!("{plates:?}"));
assert_eq!((a["added"].as_u64(), a["removed"].as_u64(), a["changed"].as_u64()), (Some(1), Some(1), Some(1)), "{a}");
// the ORACLE: Room::diff of the two as-of rooms says the same heads
let log = ws.handle.log().unwrap();
let older = mimir_room::Room::at_blocking(&log, &mimir_room::Scope::repo("fixture", Some(1))).unwrap();
let newer = mimir_room::Room::at_blocking(&log, &mimir_room::Scope::repo("fixture", None)).unwrap();
let fold = older.diff(&newer).unwrap().value;
let heads = |ids: &[String], r: &Room| {
let mut v: Vec<String> = ids
.iter()
.filter_map(|m| r.resolve(m).first().map(|&o| (m, o)))
.filter(|(_, o)| matches!(r.node(*o).kind, Some(nk::FN | nk::METHOD)) && r.node(*o).origin == Some(Origin::Local))
.map(|(m, _)| m.clone())
.collect();
v.sort();
v
};
let got = |class: &str| {
let mut v: Vec<String> = nodes.iter().filter(|n| n["class"] == class).map(|n| n["moniker"].as_str().unwrap().to_string()).collect();
v.sort();
v
};
assert_eq!(got("added"), heads(&fold.added_nodes, &newer), "added vs the fold diff");
assert_eq!(got("removed"), heads(&fold.removed_nodes, &older), "removed vs the fold diff");
assert_eq!(got("changed"), heads(&fold.changed, &newer), "changed vs the fold diff");
// the before/after hover: `dead` is in the from-sha\'s source, gone at to
let file = dead["file"].as_str().unwrap();
let before = ask(ws, json!({ "lod": "source_at", "repo": "fixture", "sha": d["from_sha"], "file": file }));
assert!(before["lines"].as_array().unwrap().iter().any(|l| l.as_str().unwrap_or_default().contains("pub fn dead")), "{before}");
let after = ask(ws, json!({ "lod": "source_at", "repo": "fixture", "sha": d["to_sha"], "file": file }));
assert!(!after["lines"].as_array().unwrap().iter().any(|l| l.as_str().unwrap_or_default().contains("pub fn dead")), "{after}");
}
/// **The MEANING JUMP** (owner 2026-10-17): for each changed function,
/// the cosine between its old and new body vectors — a reformat stays
/// cool, a rewrite glows; a body with no vector yet is said plainly.
/// Red first on the real crate: `ident` rewritten (seeded orthogonal
/// vectors → jump 1.0), `tiny` reformatted (near-identical → ~0), and
/// `norm1` changed with NO vectors (the missing share).
#[test]
fn the_meaning_jump_separates_a_rewrite_from_a_reformat() {
use crate::vector::fnvec::links::{self, Event, RunRow};
use crate::vector::fnvec::At;
let ws = fixture::real_diff();
let wh = crate::warehouse::iceberg::IcebergWarehouse::open(&ws.builds.join("warehouse")).unwrap();
let m = |d: &str| format!("rust-analyzer cargo a 0.1.0 {d}");
let (ident, tiny, norm1) = (m("ident()."), m("tiny()."), m("impl#[Point]norm1()."));
// the links the embeddings feed would have written: body hashes at
// each seq (the test owns the hashes; no embedder runs here)
let at1 = At::member("fixture", 1, "sha1");
let at2 = At::member("fixture", 2, "sha2");
let events = [
Event::plus(&at1, &ident, "H-ident-1"),
Event::plus(&at1, &tiny, "H-tiny-1"),
Event::plus(&at1, &norm1, "H-norm1-1"),
Event::plus(&at2, &ident, "H-ident-2"),
Event::plus(&at2, &tiny, "H-tiny-2"),
Event::plus(&at2, &norm1, "H-norm1-2"),
];
links::append_events(&wh, &events).unwrap();
let run = crate::vector::fnvec::Run::default();
links::append_run(&wh, &RunRow::of(&at2, "test-prof", "test-model", &run, "ok", None)).unwrap();
// vectors: ident's two bodies orthogonal (jump 1), tiny's nearly the
// same (jump ~0); norm1's are MISSING on purpose
let n = |v: [f32; 4]| {
let l = v.iter().map(|x| x * x).sum::<f32>().sqrt();
v.map(|x| x / l)
};
let (a, b) = (n([1.0, 0.0, 0.0, 0.0]), n([0.0, 1.0, 0.0, 0.0]));
let (c, d) = (n([1.0, 0.0, 0.0, 0.0]), n([1.0, 0.02, 0.0, 0.0]));
wh.block_on(crate::vector::store::append_embeddings(
&wh,
"test-prof",
4,
true,
&[("H-ident-1", &a[..]), ("H-ident-2", &b[..]), ("H-tiny-1", &c[..]), ("H-tiny-2", &d[..])],
))
.unwrap();
let reach = Reach::new(&ws.handle, &wh);
let d = super::super::answer("Viz.CodeMap", &reach, &json!({ "lod": "diff", "repo": "fixture", "from": 1 })).unwrap_or_else(|r| panic!("refused [{}] {}", r.state, r.note));
let nodes = d["nodes"].as_array().unwrap();
let head = |name: &str| nodes.iter().find(|n| n["name"] == name).unwrap_or_else(|| panic!("no head {name}: {nodes:?}"));
// the rewrite glows, the reformat stays cool
let ident_h = head("ident");
assert_eq!(ident_h["class"], "changed", "{ident_h}");
assert_eq!(ident_h["body_changed"], true, "{ident_h}");
assert!(ident_h["jump"].as_f64().unwrap() > 0.9, "{ident_h}");
let tiny_h = head("tiny");
assert!(tiny_h["jump"].as_f64().unwrap() < 0.05, "{tiny_h}");
assert_eq!(tiny_h["class"], "changed", "a body-only rewrite is a changed head: {tiny_h}");
// the missing share is said plainly
let norm1_h = head("norm1");
assert!(norm1_h["jump"].is_null(), "{norm1_h}");
assert_eq!(norm1_h["jump_missing"], "both", "{norm1_h}");
let cov = &d["jump_coverage"];
assert_eq!((cov["changed"].as_u64(), cov["measured"].as_u64(), cov["missing"].as_u64()), (Some(3), Some(2), Some(1)), "{cov}");
assert!(cov["note"].as_str().unwrap_or_default().contains("no vector yet"), "{cov}");
// the biggest meaning change leads the list, and the plate sums it
let top = d["top_jumps"].as_array().unwrap();
assert_eq!(top[0]["name"], "ident", "{top:?}");
let plates = d["plates"].as_array().unwrap();
let a_plate = plates.iter().find(|p| p["key"].as_str().unwrap_or_default().starts_with("a@")).unwrap();
assert!(a_plate["jump"].as_f64().unwrap() > 0.9, "{a_plate}");
// the payload-changed heads still equal the fold diff (body-only ones
// are the links' own truth, outside Room::diff)
assert_eq!(norm1_h["payload_changed"], true, "{norm1_h}");
assert_eq!(ident_h["payload_changed"], false, "{ident_h}");
}
#[test]
fn a_card_is_its_head_and_one_row_per_attribute_and_callee_capped() {
assert_eq!(card_height(0, 0), HEAD_H + CARD_PAD);
assert_eq!(card_height(3, 2), HEAD_H + 5.0 * ROW_H + CARD_PAD);
assert_eq!(card_height(50, 50), HEAD_H + (ATTR_ROWS_MAX + 1 + SITE_ROWS_MAX + 1) as f32 * ROW_H + CARD_PAD);
}
}