/body: full-resolution FMA anatomy viewer (V3 substrate, F16 wire, x-ray, search, server LOD)#64
Conversation
The FMA heart slice already extracts its native partonomy + subClassOf from
data/fma-heart.ttl (bfo:part_of / regional_part_of / constitutional_part_of →
part_of; rdfs:subClassOf → is_a) and lays each node's HHTL tiers out as 8:8
[container:identity] pairs — which is exactly the FacetCascade hi:lo
(part_of:is_a) shape. So the V3 migration is the contract's intended one-line
flip, not a re-carve:
- classid: 0x0000_0A01 (legacy V2) → NodeGuid::CLASSID_FMA_V3 (0x1000_0A01),
the V3 generation marker 0x1000 in the HIGH u16, canon concept 0x0A01
preserved in the LOW u16 so classid_concept_domain still routes Anatomy.
- mint: NodeGuid::new_v2(...) → NodeGuid::mint_for(classid_read_mode(c)
.tail_variant, ...), the contract's "never hardcode new vs new_v2" litmus.
V3 mints the SAME leaf·family·identity tail through new_v2; only the read
mode (ReadMode::FMA_V3) differs.
- Cargo.toml: enable lance-graph-contract feature guid-v3-tail (implies
guid-v2-tail) so CLASSID_FMA_V3 + the FMA_V3 read-mode entry are present.
Storage is byte-identical except the per-node generation marker: cmp -l of the
regenerated cockpit/public/fma.soa against the V2 bake shows exactly 125
differing bytes (= node count), every one 0x00 → 0x10 at the guid's marker
position; all tiers, classes, edges, and labels unchanged. The /fma graph
renderer reads tiers + class byte, never the classid marker, so the view is
unaffected. The bake's dual-membership proof is intact (125 nodes / 156 edges;
"Subdivision of cavity of cardiac chamber" cross-cutting 7 structures).
Note: full-workspace build was not run here — quarto-core pulls an
org-egress-blocked git dep (cscheid/runtimed) that this environment cannot
fetch. The fma bin was compiled and run in an isolated copy of its closure
(osint-bake + aiwar-ingest + the same lance-graph-contract path dep, V3
features on); verify on Railway before merge.
Claude-Session: https://claude.ai/code/session_01RhpwkHGgia2TuDFvdnuQdE
Orientation as residual-VQ on the sphere (the palette256 RVQ machinery on S²;
Fisher-2z normalized decode), comparable in O(1) LUT without materializing the
vector. Replaces a trained 3DGS quaternion (16 B) with 3 deterministic bytes.
Measured on real torso.mesh / torso.splat:
- encode: 1 B 4.87° / 2 B 0.97° (beats the 8192-dir target) / 3 B 0.073°
- render PSNR vs original (turntable, Lambert/conservative): 1 B 48.3 dB,
3 B 84.5 dB (numerically near-identical)
- compare-without-materialization vs true angle: Pearson 0.9917 / Spearman 0.9924
Adds tools/helix_orient.py (encode/decode/angle_error, self-tests to 0.073°)
and the research note capturing the unified position(HHTL)+orientation(helix)+
scale(palette256)+edges(turbovec) representation. Rust wiring of the helix
decode into the splat3d SPL3->Gaussian path is the next step (verify on Railway).
Claude-Session: https://claude.ai/code/session_01RhpwkHGgia2TuDFvdnuQdE
…drop neo4j clone Railway's `cargo build -p cockpit-server --features embed-cockpit,planner` failed and dragged needless transitive sources. Three root causes, three fixes: 1. ndarray resolved TWICE in one binary — lance-graph's local path copy AND a second `git = ".../ndarray.git"` copy declared by the q2-ndarray wrapper. Cargo cloning the git source recursively fetched ndarray's `crates/burn/upstream` submodule (AdaWorldAPI/burn.git) — an ML framework the ndarray workspace `exclude`s and never builds. Point q2-ndarray at the SAME `../../../../ndarray` path lance-graph compiles, so both unify on the one fork: no second clone, no burn submodule. 2. guid-v3-tail was enabled workspace-wide, forcing every member (incl. cockpit-server) to require a lance-graph-contract that carries it — but the Dockerfile deliberately pins lance-graph to a pre-feature SHA (the COUNT_FUSE lockstep with ogar-vocab). Only osint-bake/fma.rs mints V3, so request guid-v3-tail on osint-bake's own dep. resolver=2 keeps it out of the `-p cockpit-server` closure; cockpit needs only guid-v2-tail, which the pinned lance-graph already has. 3. The Dockerfile cloned AdaWorldAPI/neo4j-rs, referenced by no manifest (the only neo4j path is the opt-in neo4j-fallback feature, not enabled in this build). Removed the dead clone. Note: full local re-resolve was not possible (the env proxy 403s the unrelated cscheid/runtimed git relay); verified statically via Cargo.lock (which showed the two-ndarray split) and resolver=2. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RhpwkHGgia2TuDFvdnuQdE
…ay wrapper cockpit-server depended on `q2-ndarray`, a wrapper in crates/stubs/ that merely re-exports the AdaWorldAPI/ndarray fork. Wrapping buys nothing here: the whole product compiles into ONE binary from local path deps, so the wrapper only hid `ndarray::simd` / `ndarray::backend` / `ndarray::hpc` — the fork's own AVX-512 -> AVX2 -> scalar polyfill (compile-time target-cpu=x86-64-v4 flips cfg(target_feature="avx512f"); runtime simd_caps() lights AMX/BF16) — behind an indirection. Depend on `ndarray` directly, pointing at the SAME `../ndarray` checkout lance-graph already compiles, so the binary unifies on the one fork and the SIMD entry points are the fork's own, not a re-export. No second crate, no wrapper. Follow-up: delete the now-dead q2-ndarray crate and the obsolete `stubs/` umbrella (whose premise -- "local stubs for external crates that don't exist in Rust yet" -- is false: ndarray and lance-graph are real, local crates) once the in-flight SIMD audit finishes reading those files. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RhpwkHGgia2TuDFvdnuQdE
…to OGAR main The Railway build pinned lance-graph to a stale SHA (36059ce0) predating guid-v3-tail, so osint-bake (the FMA V3 bake) couldn't resolve its contract feature against it. Stop pinning to old commits: clone the repos at their branch HEADs and use the latest of everything — the tips are mutually consistent, which is the whole point. - Dockerfile: drop LANCE_GRAPH_REF; clone lance-graph at main HEAD (carries guid-v2-tail + guid-v3-tail + the 65-concept ogar_codebook mirror). The `COPY . /build/q2` before this RUN invalidates the layer on every q2 commit, so each build re-clones fresh -- the old pin's cache-bust rationale is moot. - Cargo.lock: bump the OGAR git pin (ogar-vocab + siblings, 4 entries) from 302c284 (43 concepts) to main HEAD a1fb170 (65 concepts), matching lance-graph's 65-concept codebook so the COUNT_FUSE (CODEBOOK.len() == class_ids::ALL.len()) passes at the latest of both. Verified by hand: class_ids::ALL has exactly 65 entries at a1fb170. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RhpwkHGgia2TuDFvdnuQdE
The operator-directed successor to the decimated /torso-live torso: ALL points and a real V3 address, not the 2k-concept / flat-guid / "confetti" approximations. The big binary lives in Releases (q2 `fma-body-soa-v3-v1`), never in git. - bake_body_v3.py: un-decimated twin of bake_torso_mesh.py. Keeps EVERY vertex from the 2234 BodyParts3D is_a OBJ meshes (4,209,773 verts / 6,681,030 tris) — no cell_mm clustering — and emits, per concept, the is_a ancestor sibling-rank cascade for V3 minting. - osint-bake/src/bin/body.rs: mints one CLASSID_FMA_V3 (0x1000_0A01) NodeGuid per concept via mint_for(classid_read_mode(c).tail_variant, …) — the same cascade bin/fma.rs uses for the heart slice — and fuses the V3 node table with the full-res SPM1 geometry into body.soa (BSO1 wire: header 18B | V3 table | SPM1). - BodyV3.tsx + /body route: fetches body.soa.gz from the q2 release, inflates via DecompressionStream, decodes BSO1, renders a solid Phong polygon surface — polygons, not surfels. Each vertex's node_row indexes the V3 node table. - .gitignore: cockpit/public/body.soa* + scratch-fma/ — the wire is a release asset (168 MB / 80 MB gz), kept out of the repo. Substrate verified: 1658 concepts, all classid 0x10000a01, cascade tiers populated ([depth-mixin:sibling-rank]); geometry 6.68 M tris (ALL points). tsc + vite build pass. The q2 workspace cargo build can't run in this sandbox (a sibling crate pulls the proxy-blocked runtimed git dep); body.rs compiled clean as a standalone crate against lance-graph-contract. The real workspace build runs on deploy. Generated by [Claude Code](https://claude.com/claude-code)
/body was built on the /torso-live engine (single depth-peel uFloor). Same WebGL core as /fma-body — three.js indexed mesh + Phong shader — but the wrong gate. This switches it to the compartmentalized model, on the full-res V3 data: - bake_body_v3.py: byte-19 now carries a compartment LAYER id (1 skin·2 muscle· 3 organ·4 skeleton·5 vessel·6 nerve·7 connective·8 other) via LAYER_OF, mapping the finer is_a tissues onto the same 8 layers /fma-body uses — instead of a tissue opacity. (Layer id also added to the node table for reference.) - BodyV3.tsx: FRAG gate is now `uEnabled[layer]` + `uAlpha` (per-layer on/off + solid↔transparent), with the 8 layer buttons and the solid/transparent toggle — the /fma-body UX, on 6.68 M triangles instead of the decimated 2k-concept mesh. The V3 substrate overlay (concept count + CLASSID_FMA_V3) is kept. Release assets (q2 fma-body-soa-v3-v1) re-baked with the layer byte; verified the geometry's byte-19 histogram is layers 1..6 (skin/muscle/organ/skeleton/vessel/ nerve), not opacity. tsc clean. Generated by [Claude Code](https://claude.com/claude-code)
Pins the corrected architecture after the operator's three corrections + the SIMD-polyfill finding: - V3 key is 6×(8:8) (part_of:is_a), not (depth:is_a); renderer must dispatch on classid 0x1000. - geometry must be slicer-FILLED per material (tubes/vessels), not hollow OBJ shells. - LOD via ndarray splat3d::depth_cascade (HHTL HEEL→HIP→TWIG→LEAF + Cesium SSE). - compute server-side: ndarray's wasm F32x16 backend (simd_wasm.rs) is an un-wired stub → scalar fallback ~16× too slow for client-side LOD; native is fully polyfilled, so the cascade runs in cockpit-server (x86) and streams to a thin three.js viewer. Foundation verified (scratch-fma/lodprobe, builds against ndarray features=["std","splat3d"]): body.spm1 → per-concept BlockBounds → cascade_blocks gives monotonic LOD (near 1513/1658 ProjectExact, far 1446/1658 KeepCoarse). Phases A (V3 correctness) · B (LOD pyramid) · C (slicer-fill+helix) · D (endpoint+ streaming viewer). Generated by [Claude Code](https://claude.com/claude-code)
Rebake brick 1/N for /body option-2 (operator-reviewed design, plan updated): the wire is SoA columns (MultiLaneColumn-shaped), not the BSO1 AoS vertex. bake_body_soa.py emits, at FULL resolution (4,209,773 verts / 6,681,030 tris — no decimation, the 6+M polygons; LOD pyramid is later): - per-vertex columns: body.pos (XYZ, GPU/slicer-native location), body.nrm (helix-normal source), body.row (concept index = SoA-linked identity), body.idx - per-concept table: part_of[6] + is_a[6] sibling-rank cascades (Rust packs the 8:8 address tiers), material codebook index, label codebook index, centroid - codebooks (indices, never raw text): body.labels.json, body.materials.json (6 Doppler/solid prototypes from the radiologykey abdominal-vessel signatures) Locked design captured in the plan: 3 separate 16-B GUID columns (address = part_of:is_a + classid 0x1000 + identity; location = XYZ; helix = 2 helices), material/label as codebook indices, edges as SoA-row refs, ClassView dereferences, collusion = same-location ⇒ ClassView resolution. Gouraud + bgz17 #17 palette for alpha. Verified: bake runs, part_of 838/1658 placed, materials distribute. Next brick: standalone Rust stage mints the address GUID + 2 helices (ndarray helix_orient) and assembles the SoA wire. Generated by [Claude Code](https://claude.com/claude-code)
Rebake brick 2/N. Standalone Rust stage (deps lance-graph-contract guid-v3-tail + ndarray splat3d) that consumes bake_body_soa.py's columns and assembles the BSO2 SoA wire: - mints the address GUID per concept: classid 0x1000 + (part_of:is_a) 8:8 tiers (HEEL..family) + identity tier = row → unique key (no collusion). - encodes the 2 helices per vertex via ndarray helix_orient: helix-pos (direction from origin 0,0,0; depth derivable by trig from XYZ) + helix-normal (self orientation, compressed normal). 3 bytes each, measured ~0.30° max angle error. - emits BSO2: per-concept [GUID|material|label|centroid|v-range] + per-vertex [pos|helix|row] + idx + label/material codebooks. Verified on the full-res body: 1658 concepts · 4,209,773 verts · 6,681,030 tris; GUID[0] classid=0x10000a01, 8:8 tiers, unique identity. Native x86 → helix encode runs full F32x16 SIMD (the option-2 server path). The 173 MB wire → release, not git. Standalone tool (absolute fork paths in Cargo.toml; run manually like the python bakes) — the q2 workspace can't build it in-sandbox (proxy-blocked runtimed dep). Generated by [Claude Code](https://claude.com/claude-code)
…ssid-aware Rebake brick 3/N (the visible gate: BSO2 is unreadable by the old code). BodyV3.tsx now decodes the SoA wire and renders the full 6.68 M-triangle body: - decodes BSO2 columns (address GUID · material/label codebook indices · pos · idx); joins each vertex's concept row → material codebook → colour + alpha. - dispatches on classid: asserts/shows 0x10000a01 (V3, part_of:is_a) in the HUD. - Gouraud shading from smooth normals (computeVertexNormals); the helix column is reserved for the server-side LOD, not needed to draw. - bgz17 #17 palette → alpha: transparency quantized to 17 levels per Doppler/solid material, so vessels read translucent and the solid anatomy shows through. - fetches body.soa.gz from the q2 release (BSO2, 79 MB gz / 165 MB raw), inflated via DecompressionStream — keeps ALL points, out of git. Verified: tsc clean, vite production build green. Release assets replaced with the BSO2 wire (1658 concepts · 4,209,773 verts · 6,681,030 tris). Next: slicer-fill (surface densify per material), then the cockpit-server depth_cascade LOD endpoint. Generated by [Claude Code](https://claude.com/claude-code)
/body failed with "TypeError: Failed to fetch": the browser can't fetch the GitHub release URL cross-origin (github.com/.../releases/download sends no CORS header on its redirect). Fix: the builder stage curls the release asset (fma-body-soa-v3-v1/body.soa.gz, 79 MB) into cockpit/dist/ before cargo build, so include_dir! embeds it and static_handler serves it SAME-ORIGIN at /body.soa.gz — which BodyV3 already tries first. Same pattern the cockpit already uses for torso.mesh/fma_body.mesh/torso.splat (public → dist → binary); body.soa.gz just comes from the release instead of git, so the repo stays clean. Zero cockpit-server code change (no HTTP-client dep added). Generated by [Claude Code](https://claude.com/claude-code)
…(BSO2 v3) You could only see the skin: the BSO2 renderer grouped everything into Doppler *materials* (skin/muscle/bone/organs all "solid_tissue"), so the opaque skin shell occluded the inside with no per-compartment toggle. Fixed by carrying the 8-LAYER compartment alongside the material: - body-soa-wire: adds a per-concept LAYER column (skin/muscle/organ/skeleton/vessel/ nerve/connective/other) mapped from is_a tissue; BSO2 bumped to v3. - BodyV3.tsx: decodes the layer column; the fragment shader gates on uEnabled[layer] (the /fma-body compartment model) combined with the #17-palette alpha, plus the 8-button menu + translucent/solid toggle. skin OFF by default so the anatomy shows. Verified: Rust stage re-emits (1658 concepts · 4,209,773 verts · 6,681,030 tris, classid 0x10000a01), tsc + vite build green. Release assets replaced with BSO2 v3 (79 MB gz, served same-origin via the Dockerfile pull). Generated by [Claude Code](https://claude.com/claude-code)
…weep) The visible "not a hollow shell" brick. fill_body_soa.py, a post-pass over the SoA columns (no 6 M-tri OBJ re-read): for each VESSEL concept (Doppler material 0..3 — low/high-res artery · portal · systemic venous) it extracts a centerline (PCA principal axis → project verts → bin into cross-sections → per-bin centroid + mean radius) and ring-sweeps a SOLID inner core at 0.55·radius, with radial normals (= the tube's helix-normal). The core is tagged with the source concept's row, so it inherits the vessel material/layer and renders inside the translucent #17 wall — a vessel now reads as a FILLED flowing tube (stacked translucency), not a void. Columns are truncated to exact data size (dropping the bake's 64-B tail pad) before the fill is appended, so the Rust read stays aligned. concepts.json verts/tris bumped. Verified: 662 vessels → +71,872 core verts / +133,152 core tris (6,814,182 tris total); Rust stage re-emits clean (classid 0x10000a01, helix 0.31°). Release replaced (80 MB gz); renderer unchanged (cores carry vessel material/layer). Generated by [Claude Code](https://claude.com/claude-code)
"RangeError: start offset of Float32Array should be a multiple of 4": adding the 1-byte LAYER column in BSO2 v3 shifted posOff off a 4-byte boundary, and a Float32Array *view* (new Float32Array(buf, posOff, n)) requires 4-alignment (v2 happened to be aligned; v3 isn't). Fix: read pos via buf.slice(posOff, …) — a fresh, always-4-aligned buffer, matching how the row/index columns are already read. Pure renderer fix; the wire is unchanged, no re-bake. Verified: tsc + vite build green. Generated by [Claude Code](https://claude.com/claude-code)
…8M tris The verifiable fps lever before the unverifiable server-LOD: /body drew ALL 6.8M triangles with transparent:true, killing early-Z and forcing per-fragment blending on the whole body (the 16–20 fps). Split the draw by partitioning the index: - opaque solids (skin/muscle/organ/skeleton/bone — #17 α≈1) → group 0, opaque material (depthWrite, early-Z, no blend) — the ~5M-tri majority renders fast. - translucent vessels (#17 α<1, incl. the slicer-fill cores) → group 1, transparent material (blended, drawn after) — only the minority pays the blend cost. One BufferGeometry, two draw groups, shared uniforms; no triangle dropped (keeps the full 6.8M — no decimation, per the "keep 6M+" directive). True decimation-LOD via the server depth_cascade remains the next lever if more fps is needed, but it drops points and can't be verified in-sandbox, so this opaque-split lands first. Verified: tsc + vite build green. Generated by [Claude Code](https://claude.com/claude-code)
Vessel slicer-fill (fill_body_soa.py): the lumen-core radius was the perpendicular distance from the GLOBAL PCA axis, so curved vessels ballooned where they bend away from their straight axis. Now bin the points along the axis and derive each ring from its own bin: centroid = bin's local centre (follows the curve), radius = median perpendicular distance from THAT bin centroid (robust), clamped to an absolute [RMIN, RMAX] diameter boundary (RMAX ~34 mm dia — covers the aorta, kills balloons). BF16 positions (BSO2 ver 4): the per-vertex pos column is now BF16 (3x u16 LE = 6 B/vertex, half of ver 3's 12 B f32). Conversion uses ndarray's sanctioned RNE batch path (f32_to_bf16_batch_rne) on the native bake host; the renderer widens back to f32 (bits<<16, exact — BF16 is the top 16 bits of an f32). Round-trip ~1.4 mm on a 1.7 m body, below the visual + screen-space-error floor. Wire 176->150 MB, gz 80->57.5 MB. BodyV3.tsx decodes both ver 3 and ver 4. Release asset fma-body-soa-v3-v1/body.soa.gz replaced; Dockerfile pulls it same-origin (unchanged).
The "B" brick (option-2 server-side compute). cockpit-server can't build in-sandbox (quarto-core->runtimelib is a proxy-blocked git dep), so this is de-risked three ways: 1. Verified core, standalone: scratch-fma/bodylod builds + runs here against ndarray-only and proves the cascade on the real BF16 wire — monotonic LOD (near 1521 ProjectExact/137 KeepCoarse -> far 211/1447). body_lod.rs reuses the same cascade_blocks(BlockBounds) logic (HHTL HEEL->HIP->TWIG->LEAF, O(concepts~1658), the O(1) reference, not O(verts)). 2. Tiny embedded asset: soabake bakes body.blocks (1658x16 B = 26 KB centroid+ radius per concept, in the renderer's display space), include_bytes!'d by cockpit-server — no 57 MB startup gunzip, no feature gate. Adds the splat3d ndarray feature (std-only) to cockpit-server. 3. Opt-in client (default OFF): BodyV3 keeps the full render; a "server LOD" toggle posts the throttled three.js camera to /api/body/lod, writes the per-concept HhtlAction bytes into a 1658-px R8 DataTexture, and the frag shader discards Reject concepts. Endpoint absent/erroring -> silent fallback to full render. A wrong cull can only show when the user flips the toggle. Deferred: with single-LOD geometry the cascade only distinguishes show/cull (frustum-cull whole concepts when zoomed in); switching KeepCoarse to a coarse mesh needs the L1/L2 decimation pyramid.
The /body translucent toggle previously only flipped depthWrite on the already-#17-translucent materials while uGlobalAlpha stayed 1.0, so it only "opened the heart" (the few organs whose #17 palette alpha was <1). Adopt /fma-body's model instead: a single uniform global alpha across the WHOLE body. - transparent ⇒ uGlobalAlpha 0.42 (x-ray: see through skin/muscle to organs), and BOTH draw groups (solids + #17 vessels) go translucent + depthWrite off. - solid ⇒ 1.0 (opaque solids drawn fast; #17 vessels still blend over them). - Default flips to solid (matching /fma-body) so load shows solid anatomy and the toggle reveals the x-ray. Button now reads "solid ⇄ x-ray". The opaque/transparent triangle split is kept for solid mode (fast path); only transparent mode blends the whole body, exactly as /fma-body does.
…label search P0 — out-of-body vessel tubes (hands/feet, right-thigh→toes): the slicer-fill fit ONE PCA axis over ALL of a concept's vertices and ring-swept a tube along it. When a single FMA vessel concept's mesh is several disconnected blobs (left+right hand/foot vessels under one concept; a thigh-vein row that also tags toe verts), the sweep bridged them with a solid tube THROUGH EMPTY SPACE. Fix: split each concept into spatially-connected components (grid 26-neighbour flood-fill, ~13 mm cell) and fill each component on its own centerline — disconnected blobs never bridge. 662 concepts → 715 independent components filled. Release asset rebaked + reuploaded; body.blocks (server-LOD bounds) regenerated. Colors: vertices now coloured per-concept. Vessels keep the Doppler material rgb (artery red / vein blue); every other compartment is tinted from its layer base (skeleton = bone #ebe0c7, organ = warm, muscle, skin, …) with a deterministic per-concept brightness + channel tilt, so adjacent bones/organs read as distinct shades instead of one flat fleshy colour. Search + detail: a label-codebook search box (left) filters the concept names from the wire's label column; picking one glides the camera to that concept's centroid and opens a detail side window (name, compartment, material, row, centroid) with a re-center / close. Reuses /fma-body's focus-lerp model.
… real response The LOD gate sampled a per-concept DataTexture created with RedFormat (R8). An incomplete/unsupported R8 sampler returns 0, which the frag shader reads as HhtlAction::Reject ⇒ every fragment discarded ⇒ black screen the moment LOD was toggled on. Three fixes so the feature can never black out: - RGBA8 texture (universally complete sampler) with nearest filtering; the action byte lives in .r. - Cull only after the first successful /api/body/lod response (lodReady gate), so a cold start or a slow/absent endpoint shows the full body instead of culling on empty data. - Degenerate-result guard: if the cascade rejects ~all concepts (which only happens when the camera mapping is off), show everything and warn once, rather than blacking out. Worst case LOD is now a harmless no-op; the black screen is gone.
… staircase BF16 (ver 4) positions have only a 7-bit mantissa, so a coordinate's representable step is ~2^-8 of its magnitude. Near the head (y≈0.85) that's ~3 mm — about 1/7 of the eye — so small smooth structures (eye, brain) snapped onto a ~3 mm lattice and showed a visible staircase (Treppeneffekt). Switch positions to F16 / IEEE half (ver 5): same 6 B/vertex as BF16, but a 10-bit mantissa → step ~2^-11 of magnitude → ~0.4 mm near the head (measured max error 0.21 mm over the wire), sub-visual. Coords are all in [-1,1], well inside F16's range. Bake uses ndarray's tested F16::from_f32; the renderer widens back to f32 via a 64K half→f32 LUT (O(1)/vertex). BodyV3 decodes ver 3 (f32) / 4 (BF16) / 5 (F16). gz 57.5 → 63 MB (F16 mantissa is higher-entropy than BF16; still well under f32's 80 MB). Release asset + server-LOD body.blocks regenerated.
|
Warning Review limit reached
More reviews will be available in 29 minutes and 26 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a body visualization pipeline: Python tools produce SoA and SPM1 body data, Rust code assembles BSO2 and serves LOD actions, and a new BodyV3 viewer renders the body route with server-driven concept culling. ChangesBSO2 Body Bake, Server LOD, and Viewer
Sequence Diagram(s)sequenceDiagram
participant Browser
participant BodyV3
participant cockpit-server
participant soabake
Browser->>BodyV3: open /body
BodyV3->>BodyV3: load and decode body.soa.gz
BodyV3->>cockpit-server: POST /api/body/lod
cockpit-server->>cockpit-server: cascade_blocks(camera, BlockBounds[])
cockpit-server-->>BodyV3: LodResponse {actions, tally}
BodyV3->>BodyV3: update LOD texture and shader discard state
soabake->>soabake: read SoA columns, mint GUIDs, write BSO2 + blocks
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d7718e472c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| void main(){ vNormal = normalMatrix * normal; vColor = aColor; vAlpha = aAlpha; vLayer = aLayer; vRow = aRow; | ||
| gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`; | ||
| const FRAG = ` | ||
| precision mediump float; |
There was a problem hiding this comment.
Use high precision for LOD row lookup
When server LOD is enabled on GPUs where fragment mediump has only the minimum precision, vRow cannot reliably distinguish all adjacent concept IDs in the 1,658-wide action texture. That makes (vRow + 0.5) / uLodN sample neighboring actions, so visible organs can be culled or rejected ones kept; declare highp for this shader or at least for the row/LOD-coordinate path before using concept rows as texel coordinates.
Useful? React with 👍 / 👎.
| if (st.transparent !== wasT) { | ||
| // flip the solid group into the x-ray (whole-body translucent) and back | ||
| opaqueMat.transparent = st.transparent; opaqueMat.depthWrite = !st.transparent; opaqueMat.needsUpdate = true; | ||
| transMat.depthWrite = !st.transparent; |
There was a problem hiding this comment.
Keep transparent vessel pass from writing depth
After the user toggles x-ray on and then back to solid, this sets the always-transparent vessel/material group to depthWrite=true, unlike its initial setup. Because those triangles are blended and not depth-sorted per triangle, the first transparent vessel surfaces drawn can write depth and incorrectly hide later transparent surfaces; keep this material's depth writes disabled when returning to solid mode.
Useful? React with 👍 / 👎.
| lance-graph-contract = { path = "/home/user/lance-graph/crates/lance-graph-contract", features = ["guid-v3-tail"] } | ||
| ndarray = { path = "/home/user/ndarray", default-features = false, features = ["std", "splat3d"] } |
There was a problem hiding this comment.
Use repo-relative paths for the SoA bake tool
When regenerating body.soa/body.blocks outside the original /home/user machine, this standalone Cargo project fails dependency resolution before the bake runs because both path dependencies are absolute. Since this tool is the checked-in producer of the BSO2 wire and server LOD blocks, use relative paths or workspace dependencies so the asset can be rebuilt from normal repo checkouts such as /workspace/q2 or Docker's /build/q2.
Useful? React with 👍 / 👎.
… portable bake paths Three P2s from the codex review on #64: - Server-LOD row lookup is now highp (aRow attribute + vRow varying + uLodN + the texel-center divide). mediump's minimum precision can't distinguish all 1658 concept IDs, so the LOD texture sampled neighboring actions → wrong cull on mobile GPUs. - The always-blended #17 vessel pass (transMat) no longer flips depthWrite=true when returning to solid mode; it stays depthWrite=false in both modes, so its unsorted triangles can't self-occlude later transparent surfaces. - body-soa-wire (soabake reference) uses sibling-relative path deps (../../../../../{lance-graph,ndarray}) instead of absolute /home/user paths, so the BSO2 wire + LOD blocks rebuild from any checkout (/build/q2, /workspace/q2).
There was a problem hiding this comment.
Actionable comments posted: 15
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@claude-notes/plans/2026-06-27-body-v3-server-lod-fill.md`:
- Around line 109-117: Update the BF16 wording in this plan section to match the
final F16 implementation: revise the “BF16 positions — BSO2 ver 4” heading, the
references to the BF16 wire format, and the explanation in the BodyV3.tsx /
asset notes so they consistently say F16 instead of BF16. If this section
predates the change, add a brief note that the format was subsequently upgraded
to F16 and reference the PR description, so future readers don’t mistake the
shipped wire format.
In `@cockpit/src/BodyV3.tsx`:
- Around line 430-435: The search result rows rendered in BodyV3 are mouse-only
clickable divs, so keyboard users cannot focus or activate them. Update the
match item rendering in the matches.map block to use a keyboard-focusable
control such as a button with type="button", or add equivalent tabIndex/keyboard
handlers and focus styles. Keep the existing pick(c) and setQuery('') behavior
intact when activated.
- Around line 291-292: The LOD polling logic in BodyV3 currently sets lodFail on
the first failed request and never recovers for the rest of the mount. Update
the failure handling around the LOD fetch/poll path so a transient error does
not permanently disable the feature: either add a retry/backoff strategy in the
polling flow or clear/reset lodFail when the user re-enables LOD so subsequent
polls can run again.
- Around line 363-366: The inflate helper in BodyV3 currently decides
decompression based on DecompressionStream availability instead of whether the
response is actually compressed. Update the inflate logic to inspect the
Response headers (such as Content-Encoding) or, if needed, gzip magic bytes
before decoding, and only wrap r.body with DecompressionStream in the compressed
case; otherwise fall back to r.arrayBuffer() unchanged.
In `@crates/cockpit-server/src/body_lod.rs`:
- Around line 51-58: Add a bake/version fingerprint to LodResponse so the client
can verify it is using the same concept ordering as the server’s embedded
body.blocks. Update the LodResponse struct and the server path that populates it
to include a stable bake hash/version, and make the viewer logic compare that
value before applying LOD. If the fingerprint does not match, disable LOD in the
client rather than relying on n_concepts alone.
In `@crates/osint-bake/src/bin/body.rs`:
- Around line 27-35: The legacy body bake command is still producing an
incompatible BSO1 artifact under the body.soa name, which conflicts with the
viewer’s expected BSO2 contract. Update the body.rs binary’s output path/format
so the entrypoint that writes the artifact no longer overwrites
cockpit/public/body.soa with BSO1; either switch this bin to emit the
body-soa-wire BSO2 payload or rename the legacy output to a clearly separate
artifact. Use the existing body.rs main flow and any serialization/output
helpers there to keep the documented command aligned with the new /body viewer
expectations.
In `@crates/osint-bake/tools/bake_body_soa.py`:
- Around line 115-120: The main() flow in bake_body_soa.py still falls back to a
machine-local uploads path when load_partof(scratch) returns nothing, which
hides missing part_of data and breaks portability. Remove the hardcoded
/root/.claude/uploads fallback, and make the part_of source explicit via an
argument or environment variable; if no valid source is provided, fail fast
before building the (part_of:is_a) tiers. Use the main() and load_partof() logic
as the place to enforce this.
- Around line 61-63: The new script has Ruff-blocking style issues from chained
statements and an unnecessary f-string prefix. Update the affected blocks in
bake_body_soa.py, especially around the parsing logic in the script’s main
helpers, to split each semicolon-separated statement onto its own line and
remove any f-string marker where no interpolation is used. Apply the same
cleanup to the other flagged spots so the script passes E701/E702 and F541.
- Around line 50-52: The TISSUE_MATERIAL mapping in bake_body_soa.py incorrectly
assigns "heart" to vessel material 0, which causes fill_body_soa.py to treat
heart as vessel geometry; remove that mapping so heart concepts fall back to
solid tissue unless a dedicated cardiac material exists. Update the
TISSUE_MATERIAL dictionary near the artery/vein/vessel entries to keep only true
vessel materials mapped to 0 and leave heart unmapped.
In `@crates/osint-bake/tools/bake_body_v3.py`:
- Around line 63-65: Ruff is flagging multiple semicolon-chained statements with
E702 in the parsing and face-processing logic, so split each combined statement
into separate lines to satisfy linting. Update the affected blocks in
bake_body_v3.py, especially the code around the vertex/normal parsing and the
other reported ranges, while keeping the same behavior in the relevant functions
and loops.
In `@crates/osint-bake/tools/fill_body_soa.py`:
- Around line 176-190: The appended fill geometry in fill_body_soa.py leaves
concepts[*].v_start/v_count unchanged, so the ranges serialized by body-soa-wire
no longer match the actual vertex layout. Update the concept range handling in
the fill/packing flow (around fill_one and the trunc_append/doc["verts"]
updates) so each concept’s v_start/v_count stays consistent with the appended
core data, or switch the downstream vrange usage to derive selection from
body.row instead of stale concept ranges.
- Around line 49-50: The bake tool script has multiple Ruff E702 violations
caused by semicolon-combined statements in fill_body_soa.py. Split those chained
assignments into separate lines in the affected blocks, including the covariance
accumulation section and the other reported ranges, so the script passes lint
cleanly without semicolon-separated statements.
In `@crates/osint-bake/tools/helix_orient.py`:
- Around line 71-95: The public encode/decode contract in orient.py only
supports 1-3 bytes, but encode(levels=0) and invalid code lengths/values
currently fall through to raw IndexError. Update encode and decode to validate
their API inputs up front using ValueError, rejecting levels outside 1..3, empty
codes, codes longer than 3, and any byte index that is out of range for the
selected level; use the existing encode/decode symbols to keep the checks at the
boundary before _nearest, _align, or _rot are called.
In `@Dockerfile`:
- Around line 58-60: The Dockerfile download step for body.soa.gz currently
trusts the GitHub release asset without verification. Update the existing
curl-based fetch in the build stage to validate the downloaded file using a
pinned SHA256 checksum or signature before it is kept in
/build/q2/cockpit/dist/body.soa.gz, and make the build fail if the verification
does not match.
- Around line 65-91: The Dockerfile build step is cloning lance-graph and
ndarray from moving branch HEADs, which makes the image non-reproducible. Update
the RUN step to use immutable build args or fixed refs, and have the
clone/checkouts in the same area of the Dockerfile pin lance-graph and ndarray
to exact SHAs or tags instead of default branches.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 750d0595-7f9b-4148-9a7b-adb0d877e755
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (22)
.gitignoreCargo.tomlDockerfileclaude-notes/plans/2026-06-27-body-v3-server-lod-fill.mdclaude-notes/research/2026-06-27-helix-orientation-holy-grail.mdcockpit/public/fma.soacockpit/src/BodyV3.tsxcockpit/src/main.tsxcrates/cockpit-server/Cargo.tomlcrates/cockpit-server/assets/body.blockscrates/cockpit-server/src/body_lod.rscrates/cockpit-server/src/main.rscrates/osint-bake/Cargo.tomlcrates/osint-bake/src/bin/body.rscrates/osint-bake/src/bin/fma.rscrates/osint-bake/tools/bake_body_soa.pycrates/osint-bake/tools/bake_body_v3.pycrates/osint-bake/tools/body-soa-wire/Cargo.tomlcrates/osint-bake/tools/body-soa-wire/src/main.rscrates/osint-bake/tools/fill_body_soa.pycrates/osint-bake/tools/helix_orient.pycrates/stubs/q2-ndarray/Cargo.toml
| #[derive(Debug, Serialize)] | ||
| pub struct LodResponse { | ||
| /// One `HhtlAction` byte per concept, in concept (row) order. | ||
| pub actions: Vec<u8>, | ||
| pub n_concepts: usize, | ||
| /// Tally [Reject, KeepCoarse, Refine, ProjectExact, RenderExact]. | ||
| pub tally: [usize; 5], | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Return a bake/version fingerprint in LodResponse.
BodyV3 can fall back to a release-hosted body.soa.gz, while this endpoint always computes actions against the server’s embedded body.blocks. Without a shared fingerprint, the client cannot verify that concept row ordering still matches, so server LOD can cull the wrong anatomy even when n_concepts happens to be equal. Include a bake hash/version in the response and have the viewer disable LOD on mismatch.
Also applies to: 90-90
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/cockpit-server/src/body_lod.rs` around lines 51 - 58, Add a
bake/version fingerprint to LodResponse so the client can verify it is using the
same concept ordering as the server’s embedded body.blocks. Update the
LodResponse struct and the server path that populates it to include a stable
bake hash/version, and make the viewer logic compare that value before applying
LOD. If the fingerprint does not match, disable LOD in the client rather than
relying on n_concepts alone.
| cxx += dx * dx; cxy += dx * dy; cxz += dx * dz | ||
| cyy += dy * dy; cyz += dy * dz; czz += dz * dz |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Split the semicolon-combined statements.
Ruff is flagging these new lines with E702; clean them up so lint does not fail on the bake tool.
Also applies to: 87-95, 153-153, 179-190
🧰 Tools
🪛 Ruff (0.15.18)
[error] 49-49: Multiple statements on one line (semicolon)
(E702)
[error] 49-49: Multiple statements on one line (semicolon)
(E702)
[error] 50-50: Multiple statements on one line (semicolon)
(E702)
[error] 50-50: Multiple statements on one line (semicolon)
(E702)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/osint-bake/tools/fill_body_soa.py` around lines 49 - 50, The bake tool
script has multiple Ruff E702 violations caused by semicolon-combined statements
in fill_body_soa.py. Split those chained assignments into separate lines in the
affected blocks, including the covariance accumulation section and the other
reported ranges, so the script passes lint cleanly without semicolon-separated
statements.
Source: Linters/SAST tools
| vessels += fill_one(comp, c["row"], fpx, fnx, frow, ftri, base) | ||
|
|
||
| if not fpx: | ||
| print("no vessel fill generated", file=sys.stderr); return | ||
| nfv = len(fpx)//3; nft = len(ftri)//3 | ||
| # truncate each column to its EXACT data size (drop bake's 64-byte tail pad), | ||
| # then append the fill — so the Rust read stays aligned (no padding mid-stream). | ||
| def trunc_append(name, exact, data): | ||
| with open(os.path.join(d, name), "r+b") as f: | ||
| f.truncate(exact); f.seek(exact); f.write(data) | ||
| trunc_append("body.pos", nV*12, struct.pack(f"<{len(fpx)}f", *fpx)) | ||
| trunc_append("body.nrm", nV*12, struct.pack(f"<{len(fnx)}f", *fnx)) | ||
| trunc_append("body.row", nV*4, struct.pack(f"<{len(frow)}I", *frow)) | ||
| trunc_append("body.idx", nT*12, struct.pack(f"<{len(ftri)}I", *ftri)) | ||
| doc["verts"] = nV + nfv; doc["tris"] = nT + nft |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Keep concept vertex ranges consistent after appending fill geometry.
The fill vertices are appended globally, but concepts[*].v_start/v_count remain shell-only. body-soa-wire serializes those stale ranges into BSO2, so concept focus/selection using vrange will miss the filled cores or include the wrong rows if ranges are naively expanded. Rebuild columns concept-contiguously and update each range, or make the downstream wire/render path rely solely on body.row.
🧰 Tools
🪛 ast-grep (0.44.0)
[warning] 183-183: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(os.path.join(d, name), "r+b")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(open-filename-from-request)
[warning] 190-190: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(os.path.join(d, "body.concepts.json"), "w", encoding="utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
(open-filename-from-request)
🪛 Ruff (0.15.18)
[error] 179-179: Multiple statements on one line (semicolon)
(E702)
[error] 180-180: Multiple statements on one line (semicolon)
(E702)
[error] 185-185: Multiple statements on one line (semicolon)
(E702)
[error] 185-185: Multiple statements on one line (semicolon)
(E702)
[error] 190-190: Multiple statements on one line (semicolon)
(E702)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/osint-bake/tools/fill_body_soa.py` around lines 176 - 190, The
appended fill geometry in fill_body_soa.py leaves concepts[*].v_start/v_count
unchanged, so the ranges serialized by body-soa-wire no longer match the actual
vertex layout. Update the concept range handling in the fill/packing flow
(around fill_one and the trunc_append/doc["verts"] updates) so each concept’s
v_start/v_count stays consistent with the appended core data, or switch the
downstream vrange usage to derive selection from body.row instead of stale
concept ranges.
| def encode(normal, levels=3): | ||
| """Unit direction -> tuple of `levels` byte indices (1..3). The real helix | ||
| encoder is O(1) inverse placement; this exact nearest-search is the reference.""" | ||
| code, n = [], normal | ||
| cbs = [_FULL] + _CAPS | ||
| frames = [] | ||
| for lvl in range(levels): | ||
| c = _nearest(n, cbs[lvl]) | ||
| code.append(c) | ||
| if lvl + 1 < levels: | ||
| k, t = _align(cbs[lvl][c]) | ||
| n = _rot(n, k, t) | ||
| frames.append((k, t)) | ||
| return tuple(code) | ||
|
|
||
|
|
||
| def decode(code): | ||
| """byte indices -> reconstructed unit direction.""" | ||
| cbs = [_FULL] + _CAPS | ||
| d = cbs[len(code) - 1][code[-1]] | ||
| for lvl in range(len(code) - 2, -1, -1): | ||
| k, t = _align(cbs[lvl][code[lvl]]) | ||
| d = _rot(d, k, -t) | ||
| m = math.sqrt(_dot(d, d)) or 1.0 | ||
| return (d[0]/m, d[1]/m, d[2]/m) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Validate the public 1-3 byte contract at the API boundary.
encode(levels=0) currently returns (), and decode(()) / levels>3 fail later with raw IndexError. Since this API only supports 1-3 bytes, reject invalid lengths and byte values up front with ValueError.
Suggested fix
def encode(normal, levels=3):
"""Unit direction -> tuple of `levels` byte indices (1..3). The real helix
encoder is O(1) inverse placement; this exact nearest-search is the reference."""
+ if not 1 <= levels <= 3:
+ raise ValueError("levels must be between 1 and 3")
code, n = [], normal
cbs = [_FULL] + _CAPS
frames = []
@@
def decode(code):
"""byte indices -> reconstructed unit direction."""
+ if not 1 <= len(code) <= 3:
+ raise ValueError("code must contain between 1 and 3 bytes")
+ if any(not 0 <= b < _K for b in code):
+ raise ValueError("code bytes must be in [0, 255]")
cbs = [_FULL] + _CAPS
d = cbs[len(code) - 1][code[-1]]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def encode(normal, levels=3): | |
| """Unit direction -> tuple of `levels` byte indices (1..3). The real helix | |
| encoder is O(1) inverse placement; this exact nearest-search is the reference.""" | |
| code, n = [], normal | |
| cbs = [_FULL] + _CAPS | |
| frames = [] | |
| for lvl in range(levels): | |
| c = _nearest(n, cbs[lvl]) | |
| code.append(c) | |
| if lvl + 1 < levels: | |
| k, t = _align(cbs[lvl][c]) | |
| n = _rot(n, k, t) | |
| frames.append((k, t)) | |
| return tuple(code) | |
| def decode(code): | |
| """byte indices -> reconstructed unit direction.""" | |
| cbs = [_FULL] + _CAPS | |
| d = cbs[len(code) - 1][code[-1]] | |
| for lvl in range(len(code) - 2, -1, -1): | |
| k, t = _align(cbs[lvl][code[lvl]]) | |
| d = _rot(d, k, -t) | |
| m = math.sqrt(_dot(d, d)) or 1.0 | |
| return (d[0]/m, d[1]/m, d[2]/m) | |
| def encode(normal, levels=3): | |
| """Unit direction -> tuple of `levels` byte indices (1..3). The real helix | |
| encoder is O(1) inverse placement; this exact nearest-search is the reference.""" | |
| if not 1 <= levels <= 3: | |
| raise ValueError("levels must be between 1 and 3") | |
| code, n = [], normal | |
| cbs = [_FULL] + _CAPS | |
| frames = [] | |
| for lvl in range(levels): | |
| c = _nearest(n, cbs[lvl]) | |
| code.append(c) | |
| if lvl + 1 < levels: | |
| k, t = _align(cbs[lvl][c]) | |
| n = _rot(n, k, t) | |
| frames.append((k, t)) | |
| return tuple(code) | |
| def decode(code): | |
| """byte indices -> reconstructed unit direction.""" | |
| if not 1 <= len(code) <= 3: | |
| raise ValueError("code must contain between 1 and 3 bytes") | |
| if any(not 0 <= b < _K for b in code): | |
| raise ValueError("code bytes must be in [0, 255]") | |
| cbs = [_FULL] + _CAPS | |
| d = cbs[len(code) - 1][code[-1]] | |
| for lvl in range(len(code) - 2, -1, -1): | |
| k, t = _align(cbs[lvl][code[lvl]]) | |
| d = _rot(d, k, -t) | |
| m = math.sqrt(_dot(d, d)) or 1.0 | |
| return (d[0]/m, d[1]/m, d[2]/m) |
🧰 Tools
🪛 Ruff (0.15.18)
[warning] 75-75: Consider [_FULL, *_CAPS] instead of concatenation
Replace with [_FULL, *_CAPS]
(RUF005)
[warning] 89-89: Consider [_FULL, *_CAPS] instead of concatenation
Replace with [_FULL, *_CAPS]
(RUF005)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/osint-bake/tools/helix_orient.py` around lines 71 - 95, The public
encode/decode contract in orient.py only supports 1-3 bytes, but
encode(levels=0) and invalid code lengths/values currently fall through to raw
IndexError. Update encode and decode to validate their API inputs up front using
ValueError, rejecting levels outside 1..3, empty codes, codes longer than 3, and
any byte index that is out of range for the selected level; use the existing
encode/decode symbols to keep the checks at the boundary before _nearest,
_align, or _rot are called.
| RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.soa.gz \ | ||
| -o /build/q2/cockpit/dist/body.soa.gz \ | ||
| && ls -lh /build/q2/cockpit/dist/body.soa.gz |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Verify the downloaded body.soa.gz before embedding it.
This pulls a release binary straight into the image with no integrity check, so a re-uploaded or corrupted asset would ship unnoticed. Pin a SHA256 (or signature) and fail the build on mismatch.
Suggested hardening
+ARG BODY_SOA_GZ_SHA256=<expected-sha256>
RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.soa.gz \
-o /build/q2/cockpit/dist/body.soa.gz \
+ && echo "${BODY_SOA_GZ_SHA256} /build/q2/cockpit/dist/body.soa.gz" | sha256sum -c - \
&& ls -lh /build/q2/cockpit/dist/body.soa.gz📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.soa.gz \ | |
| -o /build/q2/cockpit/dist/body.soa.gz \ | |
| && ls -lh /build/q2/cockpit/dist/body.soa.gz | |
| ARG BODY_SOA_GZ_SHA256=<expected-sha256> | |
| RUN curl -fSL https://github.com/AdaWorldAPI/q2/releases/download/fma-body-soa-v3-v1/body.soa.gz \ | |
| -o /build/q2/cockpit/dist/body.soa.gz \ | |
| && echo "${BODY_SOA_GZ_SHA256} /build/q2/cockpit/dist/body.soa.gz" | sha256sum -c - \ | |
| && ls -lh /build/q2/cockpit/dist/body.soa.gz |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Dockerfile` around lines 58 - 60, The Dockerfile download step for
body.soa.gz currently trusts the GitHub release asset without verification.
Update the existing curl-based fetch in the build stage to validate the
downloaded file using a pinned SHA256 checksum or signature before it is kept in
/build/q2/cockpit/dist/body.soa.gz, and make the build fail if the verification
does not match.
| # lance-graph + ndarray are cloned at their BRANCH HEAD (latest) — NOT a pinned, | ||
| # stale SHA. The repos at their tips are mutually consistent, so "use the latest of | ||
| # everything" is the rule: a pinned-old lance-graph (36059ce0) is exactly what | ||
| # lacked `guid-v3-tail` and broke the build. The `COPY . /build/q2` above changes on | ||
| # every q2 commit, invalidating this RUN layer too, so each build re-clones fresh | ||
| # (no stale-cache problem the old pin was guarding against). | ||
| # | ||
| # Sibling checkouts the path deps resolve against: | ||
| # /build/lance-graph → lance-graph @ main HEAD — carries guid-v2-tail + | ||
| # guid-v3-tail and the 65-concept ogar_codebook mirror. | ||
| # /build/ndarray → the REAL AdaWorldAPI/ndarray fork, consumed by BOTH | ||
| # lance-graph (../../../ndarray) AND q2-ndarray | ||
| # (../../../../ndarray). `--depth 1` WITHOUT | ||
| # --recurse-submodules: ndarray's workspace `exclude`s | ||
| # crates/burn, so the burn submodule (AdaWorldAPI/burn.git) | ||
| # is never needed — leaving it unfetched is correct. | ||
| # | ||
| # COUNT_FUSE: lance-graph-ogar asserts (E0080 on mismatch) | ||
| # CODEBOOK.len() == ogar_vocab::class_ids::ALL.len(). Both move together at HEAD — | ||
| # lance-graph main's 65-concept codebook mirror matches OGAR main's 65-concept vocab; | ||
| # q2's Cargo.lock pins ogar-vocab to OGAR main HEAD (a1fb170). Always bump the two | ||
| # repos' HEADs together, never one alone. | ||
| # | ||
| # neo4j-rs is intentionally NOT cloned — a discarded Neo4j-GUI experiment referenced | ||
| # by no manifest; the only neo4j path is the opt-in `neo4j-fallback` (crates.io neo4rs). | ||
| RUN git clone --depth 1 https://github.com/AdaWorldAPI/lance-graph.git \ | ||
| && git clone --depth 1 https://github.com/AdaWorldAPI/ndarray.git |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Pin lance-graph and ndarray to immutable revisions.
Cloning default-branch HEAD makes this image non-reproducible: an upstream push can break a previously green q2 commit with no change in this repo. Keep the refs in build args and check out exact SHAs or tags.
Suggested fix
+ARG LANCE_GRAPH_REF=<sha-or-tag>
+ARG NDARRAY_REF=<sha-or-tag>
-RUN git clone --depth 1 https://github.com/AdaWorldAPI/lance-graph.git \
- && git clone --depth 1 https://github.com/AdaWorldAPI/ndarray.git
+RUN git clone https://github.com/AdaWorldAPI/lance-graph.git /build/lance-graph \
+ && git -C /build/lance-graph checkout "$LANCE_GRAPH_REF" \
+ && git clone https://github.com/AdaWorldAPI/ndarray.git /build/ndarray \
+ && git -C /build/ndarray checkout "$NDARRAY_REF"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # lance-graph + ndarray are cloned at their BRANCH HEAD (latest) — NOT a pinned, | |
| # stale SHA. The repos at their tips are mutually consistent, so "use the latest of | |
| # everything" is the rule: a pinned-old lance-graph (36059ce0) is exactly what | |
| # lacked `guid-v3-tail` and broke the build. The `COPY . /build/q2` above changes on | |
| # every q2 commit, invalidating this RUN layer too, so each build re-clones fresh | |
| # (no stale-cache problem the old pin was guarding against). | |
| # | |
| # Sibling checkouts the path deps resolve against: | |
| # /build/lance-graph → lance-graph @ main HEAD — carries guid-v2-tail + | |
| # guid-v3-tail and the 65-concept ogar_codebook mirror. | |
| # /build/ndarray → the REAL AdaWorldAPI/ndarray fork, consumed by BOTH | |
| # lance-graph (../../../ndarray) AND q2-ndarray | |
| # (../../../../ndarray). `--depth 1` WITHOUT | |
| # --recurse-submodules: ndarray's workspace `exclude`s | |
| # crates/burn, so the burn submodule (AdaWorldAPI/burn.git) | |
| # is never needed — leaving it unfetched is correct. | |
| # | |
| # COUNT_FUSE: lance-graph-ogar asserts (E0080 on mismatch) | |
| # CODEBOOK.len() == ogar_vocab::class_ids::ALL.len(). Both move together at HEAD — | |
| # lance-graph main's 65-concept codebook mirror matches OGAR main's 65-concept vocab; | |
| # q2's Cargo.lock pins ogar-vocab to OGAR main HEAD (a1fb170). Always bump the two | |
| # repos' HEADs together, never one alone. | |
| # | |
| # neo4j-rs is intentionally NOT cloned — a discarded Neo4j-GUI experiment referenced | |
| # by no manifest; the only neo4j path is the opt-in `neo4j-fallback` (crates.io neo4rs). | |
| RUN git clone --depth 1 https://github.com/AdaWorldAPI/lance-graph.git \ | |
| && git clone --depth 1 https://github.com/AdaWorldAPI/ndarray.git | |
| # lance-graph + ndarray are cloned at their BRANCH HEAD (latest) — NOT a pinned, | |
| # stale SHA. The repos at their tips are mutually consistent, so "use the latest of | |
| # everything" is the rule: a pinned-old lance-graph (36059ce0) is exactly what | |
| # lacked `guid-v3-tail` and broke the build. The `COPY . /build/q2` above changes on | |
| # every q2 commit, invalidating this RUN layer too, so each build re-clones fresh | |
| # (no stale-cache problem the old pin was guarding against). | |
| # | |
| # Sibling checkouts the path deps resolve against: | |
| # /build/lance-graph → lance-graph @ main HEAD — carries guid-v2-tail + | |
| # guid-v3-tail and the 65-concept ogar_codebook mirror. | |
| # /build/ndarray → the REAL AdaWorldAPI/ndarray fork, consumed by BOTH | |
| # lance-graph (../../../ndarray) AND q2-ndarray | |
| # (../../../../ndarray). `--depth 1` WITHOUT | |
| # --recurse-submodules: ndarray's workspace `exclude`s | |
| # crates/burn, so the burn submodule (AdaWorldAPI/burn.git) | |
| # is never needed — leaving it unfetched is correct. | |
| # | |
| # COUNT_FUSE: lance-graph-ogar asserts (E0080 on mismatch) | |
| # CODEBOOK.len() == ogar_vocab::class_ids::ALL.len(). Both move together at HEAD — | |
| # lance-graph main's 65-concept codebook mirror matches OGAR main's 65-concept vocab; | |
| # q2's Cargo.lock pins ogar-vocab to OGAR main HEAD (a1fb170). Always bump the two | |
| # repos' HEADs together, never one alone. | |
| # | |
| # neo4j-rs is intentionally NOT cloned — a discarded Neo4j-GUI experiment referenced | |
| # by no manifest; the only neo4j path is the opt-in `neo4j-fallback` (crates.io neo4rs). | |
| ARG LANCE_GRAPH_REF=<sha-or-tag> | |
| ARG NDARRAY_REF=<sha-or-tag> | |
| -RUN git clone --depth 1 https://github.com/AdaWorldAPI/lance-graph.git \ | |
| RUN git clone https://github.com/AdaWorldAPI/lance-graph.git /build/lance-graph \ | |
| && git -C /build/lance-graph checkout "$LANCE_GRAPH_REF" \ | |
| && git clone https://github.com/AdaWorldAPI/ndarray.git /build/ndarray \ | |
| && git -C /build/ndarray checkout "$NDARRAY_REF" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Dockerfile` around lines 65 - 91, The Dockerfile build step is cloning
lance-graph and ndarray from moving branch HEADs, which makes the image
non-reproducible. Update the RUN step to use immutable build args or fixed refs,
and have the clone/checkouts in the same area of the Dockerfile pin lance-graph
and ndarray to exact SHAs or tags instead of default branches.
…ry, a11y CodeRabbit findings on #64: - Heart no longer classified as vessel material 0: it's a chambered organ, so the slicer-fill was sweeping a PCA-centerline lumen rod through it. Now falls through to solid_tissue (22 concepts artery→solid; fill 715→693 components). Wire + LOD blocks rebaked + reuploaded. - Decompress by the payload, not DecompressionStream support: sniff the gzip magic (1f 8b) so a browser/CDN that already decoded Content-Encoding doesn't get double-inflated, and a browser without DecompressionStream errors clearly instead of feeding gzip bytes to the decoder. - Server-LOD no longer permanently disabled after one failed poll: toggling LOD off clears the failure flag so re-enabling retries. - Search-result rows are now keyboard-focusable <button type="button"> elements instead of mouse-only <div>s. - Plan doc: marked the BF16 (ver 4) section SUPERSEDED by F16 (ver 5) to match the shipped wire.
The liver is modelled as Couinaud "hepatovenous segment II–IX" — named for venous drainage, so tissue_of tagged all 8 (the bulk of the liver, e.g. segment VIII = 21801 verts) as vein → material 3 (blue) AND slicer-filled as tubes. The liver rendered as a blue, tube-filled vessel blob sitting above the colon. Reclassify "hepatovenous segment N" → liver tissue → solid_tissue material + organ compartment. The true hepatic/portal veins (all carry "vein" in their name, e.g. "... segmental tributary of right hepatic vein") are untouched and stay vessels. 8 concepts vein→solid; fill 693→685 components. Wire + LOD blocks rebaked + reuploaded.
…script
Addresses the ChatGPT visual audit:
- Eyes no longer blue: the iris and choroid (eyeball vascular layers) were tagged
'vessel' → material 3 (blue). Reclassified to organ (the brain's "choroid plexus"
is excluded). Sclera/cornea/retina/vitreous/lens were already solid.
- UI label "nerve" → "nervous" (the compartment is the nervous system; today it's
brain-only, hierarchy to follow).
- New crates/osint-bake/tools/audit_body_semantics.py — a QA gate:
* QA-1 organ-scale vessels (blob in a vessel material — the heart/liver/iris class)
* QA-2 floating/misplaced geometry (non-bilateral split; caught "right fibular vein"
with a chunk in the thigh far from its calf body)
* QA-3 per-organ compartment smoke tests (liver/eyeball→organ, brain→nervous,
femur→skeleton, biceps→muscle, aorta/vena cava→vessel) — exit non-zero on fail.
All QA-3 smoke tests pass after the liver + eye fixes.
x-ray/solid already only affects transparency (uGlobalAlpha/depthWrite), never
compartment visibility (uEnabled) — verified, no change needed. Wire + LOD blocks
rebaked + reuploaded.
Summary
Adds
/body— a full-resolution FMA human-anatomy viewer (4.3M verts / 6.8M tris) rendered as polygons on the V3 substrate (classid 0x1000_0A01, the(part_of:is_a)8:8 cascade), fed by an offline bake and drawn client-side in three.js/WebGL.Wire / bake (offline,
crates/osint-bake/tools/+ standalonesoabake)(part_of:is_a)cascade + identity; location = XYZ; 2 helices) — stores codebook indices, never raw values.RMAXdiameter clamp (no "inflatable tubes"), and connected-component splitting so disconnected blobs sharing one concept never bridge (kills the out-of-body hand/foot/thigh→toe tubes).F16::from_f32.Renderer (
cockpit/src/BodyV3.tsx)Server LOD (
cockpit-server, opt-in)/api/body/lod: HHTLcascade_blocks(ndarraysplat3d::depth_cascade) over 1658 bakedBlockBounds(26 KB embedded asset), returns per-concept action bytes. Client gates draw via an RGBA8 LOD texture, default off, with cold-start + degenerate-result guards so it can never black out.Deploy
body.soa.gzships as a GitHub release asset, pulled intocockpit/distat Docker build and served same-origin (the release redirect sends no CORS header).Verification
tsc && vite build): passes clean (104 modules).depth_cascadeAPIbody_lod.rsuses).cargo build -p cockpit-server(itsruntimedgit dep is proxy-blocked) — this PR's CI / the Railway build is the real end-to-end check. All changes are additive; the toolchain/MSRV setup (q2 pinsnightly-2026-04-28, satisfying ndarray's 1.95) is pre-existing.🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes