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

nordisk/nornir

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 &quotient.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(&region_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 &region_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 &quotient.edges {
            if let (Some(f), Some(t)) = (group_plate(&quotient, e.from), group_plate(&quotient, 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);
    }
}