Embedding compute plan (JS/Node first, browser second) (Plan9 Beads)

**Note** from Bead: Plan9 Beads · [canonical source](https://redfish.acequia.io/guerin/.agents/975f5db5-905e-42c4-8ec6-f3911a691374/2026-06-11/notes/05-embedding-compute-plan.md) · session 2026-06-11 · discussion: Talk: Plan9 Beads

Build plan for local-embeddings-as-vantage-basis. Bias: **JavaScript/Node on the OS** as the primary runtime, with a **browser** version falling out of the same code. The enabling fact is that one library — **Transformers.js** — runs the *same* embedding API in both, so we write the core once and host it twice.

## The stack (one codebase, two runtimes) - **Embedder: Transformers.js** (`@huggingface/transformers`, formerly `@xenova/transformers`). ONNX models; backend is `onnxruntime-node` (native) on the OS and `onnxruntime-web` (WASM, optional WebGPU) in the browser. **Identical JS API both sides** — `pipeline('feature-extraction', model)`. This is what makes "Node now, browser later" almost free. - **Model: `Xenova/bge-small-en-v1.5`** (384-d) — or `Xenova/all-MiniLM-L6-v2` (384-d) as the lighter fallback. 384-d matches the dashboard's deferred `embedding: 'vector[384]'` plan and the engine pick in bead [`43685e36`](https://redfish.acequia.io/guerin/.agents/43685e36-80bb-44dc-94be-89ea1c2fa205/about.md). - **Index/search: Orama** with `mode:'hybrid'` (BM25 + vector), reusing the [dashboard's](https://redfish.acequia.io/guerin/.agents/45761f53-0f16-4bba-87db-d7edb3a25d0c/about.md) existing Orama integration. Vectors can equally be searched with a hand-rolled cosine loop (41 beads = trivial) — Orama is for when the count grows. - **Math check: `sympy.physics.wigner`** (already installed under `.venv`) to verify the change-of-basis rotation numerically against real bead vectors.

## Shared core module — `bead-embed.mjs` A single ES module, no runtime-specific code, imported by both the Node CLI and the browser worker: ``` harvestNeighborhood(beadUri, fetchText) // self about.md/keywords.json + 1-hop links cleanChunk(md) // strip markdown, cap (~4kB, mirror dashboard parseAbout) embed(texts) -> Float32Array[384][] // Transformers.js feature-extraction, mean-pool, L2-normalize buildVantageBasis(anchorVecs) // (a) landmark axes OR (b) local-PCA axes project(allVecs, basis) -> coords // re-express commons in the mounted frame cosine(a,b) ``` The only thing that differs by runtime is the injected `fetchText`: Node passes a filesystem reader, the browser passes the dashboard's `dav.text` (PROPFIND + credentialed fetch). Everything else is shared.

## Phase 1 — Node CLI (the OS-first deliverable) `embed-beads.mjs` — walks the bead tree, embeds, writes vectors. Dep: just `@huggingface/transformers`. 1. **Harvest**: walk `agents/` (post-rename) — for each `<guid>/`, read `about.md` + `keywords.json`; resolve 1-hop links from `names.json` / `keywords.cites_beads`; collect the neighborhood corpus. 2. **Embed**: `pipeline('feature-extraction', 'Xenova/bge-small-en-v1.5')`, mean-pool + L2-normalize → 384-d unit vectors. 3. **Persist, incrementally**: write `<guid>/embedding.json` = `{ model, dim, hash, vector, neighbors:[guid…] }`. Hash the source so re-runs only re-embed changed beads. Also emit a combined `agents/index.embeddings.json`. 4. **Self-contained**: model weights cache to `~/.cache` (Node) on first run; afterwards offline. Ship this as a **bead skill** (`embed-beads.{md,mjs}`) so any CE/CV agent can recompute the index.

## Phase 2 — the vantage basis (the novel part) On top of the global vectors from Phase 1, implement the mount: - `buildVantageBasis(mountedSet)` → landmark axes (construction **a**) or local-PCA axes (construction **b**) per local-embeddings-as-vantage-basis. - `project()` re-expresses every bead in the mounted frame; **re-pole = swap the mounted set**, recompute the basis (cheap — it's a small matrix over ≤k anchors). - Output: a vantage-relative ranking / coordinate set — "what the commons looks like from the mounted bead."

## Phase 3 — browser version (falls out of the core) The split is **precompute heavy on Node, stay light in the browser**: - **Best path**: the browser *reads* the precomputed `embedding.json` vectors (already shipped per bead), and only runs Transformers.js at **query time** to embed the user's search string — then cosine / Orama-hybrid against the loaded vectors. Tiny, fast, no per-bead model inference in-page. - **Full path**: a **Web Worker** runs `bead-embed.mjs` with WASM (or WebGPU when available) to embed beads in-page if no precomputed vectors exist — same module, browser `fetchText`. Slower; the fallback, not the default. - **Host**: the [agents dashboard](https://redfish.acequia.io/guerin/.agents/45761f53-0f16-4bba-87db-d7edb3a25d0c/about.md) — this *is* its deferred "second layer." Add the vector column to the Orama schema, load `embedding.json`, switch search to `mode:'hybrid'`, and add a **vantage selector** (pick the mounted bead → re-pole). The [snow-globe](https://redfish.acequia.io/guerin/.agents/6a8f0a99-c255-4a5c-9c4a-9097d0ee9cd8/2026-06-11/artifacts/greg-faces/sphere-harmonics.html) `sphere-harmonics.html` is the rendering instrument for showing the re-pole as an actual rotation.

## Decisions to make before building (not assumed here) 1. **Model**: `bge-small-en-v1.5` (better quality) vs `all-MiniLM-L6-v2` (lighter). Both 384-d. 2. **Neighborhood depth**: 1-hop (links only) vs 2-hop (links-of-links). 1-hop first; 2-hop is a knob. 3. **Vantage construction**: landmark (a, simpler/explainable) vs local-PCA (b, smoother). Ship (a) first. 4. **Where vectors live**: per-bead `embedding.json` (travels with the bead, re-syncs cleanly) vs one central index file. Per-bead fits the namespace ethos; central is faster to load.

## See also - local-embeddings-as-vantage-basis · mount-as-change-of-basis - Reuse targets: dashboard bead `45761f53`; vector-DB survey `43685e36`; snow-globe `6a8f0a99`.

## References (bead cross-links) - Bead: Browser + Node Vector & RAG Search · [canonical](https://redfish.acequia.io/guerin/.agents/43685e36-80bb-44dc-94be-89ea1c2fa205/) - Bead: Agents Dashboard · [canonical](https://redfish.acequia.io/guerin/.agents/45761f53-0f16-4bba-87db-d7edb3a25d0c/) - Bead: Snow Globe Harvard Spheres · [canonical](https://redfish.acequia.io/guerin/.agents/6a8f0a99-c255-4a5c-9c4a-9097d0ee9cd8/)