Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

Plan path: `.claude/plans/splat-native-ultrasound-v1.md`. Companions: ndarray `.claude/plans/splat-native-ultrasound-simd-substrate-v1.md`; OGAR `docs/SPLAT-NATIVE-CUSTOMER.md`; MedCare-rs `.claude/handovers/2026-06-05-splat-native-medcare-hipaa-wire.md`. Customer of OGAR PR #30 §6 FMA bones-rendering litmus + ADR-022 SaMD audit-controls evidence base.

| D-id | Title | Crate(s) / repo | ~LOC | Risk | Status | PR / Evidence |
|---|---|---|---|---|---|---|
| D-SPLAT-1 | `Gaussian3D` carrier (`mu`/`sigma_packed`/`amplitude`/`opacity`/`sh[16]`/`frame_idx`/`class_id`; 80 B/row) | `lance-graph-contract::splat` | 120 | LOW | **Queued — P1 sprint 1-2** | gates on `MailboxSoAHeader` (D-MBX-10) or own feature flag |
| D-SPLAT-2 | `ndarray::simd::splat` batch ops — `batched_cholesky_3x3` / `batched_mahalanobis` / `batched_opacity_blend` / `batched_sh_eval_l3` / `batched_se3_transform`; all three backends (AVX-512/NEON/scalar) | `ndarray::src/simd_splat.rs` | 600 | MED | **Queued — P1 sprint 1-2** | foundation; none |
| D-SPLAT-3 | `SplatBatch<N>` SoA carrier (per-column slices for SIMD sweep; inherits MailboxSoAHeader versioning) | `lance-graph-contract::splat` | 150 | LOW | **Queued — P1 sprint 1-2** | gates on D-SPLAT-1 |
| D-SPLAT-4 | SH-aware palette extension in `crates/bgz17` (256×256×2B compose table; SH-basis-id per centroid) | `bgz17::sh_palette` | 250 | MED | **Queued — P3 sprint 4-5** | gates on D-SPLAT-1 |
| D-SPLAT-5 | Splat-to-splat registration math — Σ-sandwich Mahalanobis ICP + SE(3) Levenberg-Marquardt | `lance-graph::splat::registration` | 400 | HIGH | **Queued — P4 sprint 6-7** | gates on D-SPLAT-2 + D-SPLAT-3 |
| D-SPLAT-6 | `crates/splat-fit` engine — RF/IQ → beamformed → local-maxima → PSF estimate → SH projection → emit Gaussian3D batch | `crates/splat-fit` (new standalone, 0-dep, ndarray-hpc feature) | 1500 | HIGH | **Queued — P2 sprint 3** | gates on D-SPLAT-1 + D-SPLAT-2 + OQ-SPLAT-3 |
| D-SPLAT-7 | Splat actors — `SplatFitActor`/`PoseAccumulatorActor`/`RegistrationActor`, each owns one `MailboxSoA<Gaussian3D>`; consumes bardioc #17 Rubicon kanban verbatim | `crates/splat-actors` (or `ractor_actors`) | 500 | MED | **Queued — P3 sprint 4-5** | gates on D-SPLAT-3 + D-SPLAT-6 + bardioc #17 (shipped) |
| D-SPLAT-8 | FMA atlas hydrator — TTL → `fma_class.lance` + `fma_relation.lance` + `fma_atlas_splat.lance` (~150M Gaussians full body) | `lance-graph-ontology` + `crates/fma-hydrator` | 800 | HIGH | **Queued — P4 sprint 7-8** | gates on OGAR PR #30 Phase 8 + D-SPLAT-3 + ndarray PR #189 (shipped) |
| D-SPLAT-9 | `fma_blueprint::style_recipe` D-Atom catalogue (AnatomicalRegion, OrganSystem, Innervation, Vasculature, Joint, Muscle, Bone, OrganParenchyma, Tract); mirrors PR #433 Odoo pattern | `lance-graph-ontology::fma_blueprint` | 400 | LOW | **Queued — P4 sprint 7-8** | gates on D-SPLAT-8 |
| D-SPLAT-10 | `memory.ultrasound_frame.lance` + `memory.ultrasound_splat.lance` datasets via `soa_mapping.rs`; new `SensitivityReason::UltrasoundRawPHI`/`UltrasoundAnonymized` variants in `column_mask_bridge` | MedCare-rs `crates/medcare-analytics` | 250 | MED | **Queued — P5 sprint 9-10** | gates on D-SPLAT-3 + MedCare PR #162 (shipped) |
| D-SPLAT-11 | `commit_event` audit chain for splat ingest via `LanceMembrane::commit_event` (callcenter PR #467, sole-writer membrane); `KnowableFromStore::register("ogit-medcare/ultrasound_ingest", Some(ddl_hint))` | MedCare-rs `crates/medcare-analytics` | 100 | LOW | **Queued — P5 sprint 9-10** | gates on D-SPLAT-10 + PR #467 (shipped) + OGAR #25/#31 (shipped) |
| D-SPLAT-12 | AR splat renderer — HoloLens OpenXR (clinical AR target) + Cesium ion + Three.js (browser fallback) + headless PNG (regression); CPU does math, GPU only paints | `crates/splat-render` (new) | 1200 | HIGH | **Queued — P6 sprint 11-13** | gates on D-SPLAT-2 + D-SPLAT-3 + D-SPLAT-5 |
| D-SPLAT-13 | IMU/POSE 4D accumulator — VIO against splat features at IMU rate (~200 Hz); splat-corrected pose at frame rate (~30 Hz); Planning-column readiness at t = −550ms | `splat-actors::PoseAccumulatorActor` | 200 | MED | **Queued — P3 sprint 4-5** | gates on D-SPLAT-7 |
| D-SPLAT-14 | SaMD documentation track — research-tool → clinical-study → Class IIa (IEC 62366 / IEC 80001 / ISO 14971 / IVD-MDR Rule 11). ADR-022 firewall IS the audit-controls evidence base | `q2`/`quarto` or `docs/` | 600 | LOW | **Queued — P7 sprint 14+ (parallel through P4-P6)** | gates on none architecturally; v1/v2/v3 phased |
| D-id | Title | Crate(s) / repo | ~LOC | Risk | Sprint | Status | PR / Evidence |
|---|---|---|---|---|---|---|---|
| D-SPLAT-1 | `Gaussian3D` carrier (`mu`/`sigma_packed`/`amplitude`/`opacity`/`sh[16]`/`frame_idx`/`class_id`; 80 B/row) | `lance-graph-contract::splat` | 120 | LOW | P1 sprint 1-2 | **Queued** | gates on `MailboxSoAHeader` (D-MBX-10) or own feature flag |
| D-SPLAT-2 | `ndarray::simd::splat` batch ops — `batched_cholesky_3x3` / `batched_mahalanobis` / `batched_opacity_blend` / `batched_sh_eval_l3` / `batched_se3_transform`; all three backends (AVX-512/NEON/scalar) | `ndarray::src/simd_splat.rs` | 600 | MED | P1 sprint 1-2 | **Queued** | foundation; none |
| D-SPLAT-3 | `SplatBatch<N>` SoA carrier (per-column slices for SIMD sweep; inherits MailboxSoAHeader versioning) | `lance-graph-contract::splat` | 150 | LOW | P1 sprint 1-2 | **Queued** | gates on D-SPLAT-1 |
| D-SPLAT-4 | SH-aware palette extension in `crates/bgz17` (256×256×2B compose table; SH-basis-id per centroid) | `bgz17::sh_palette` | 250 | MED | P3 sprint 4-5 | **Queued** | gates on D-SPLAT-1 |
| D-SPLAT-5 | Splat-to-splat registration math — Σ-sandwich Mahalanobis ICP + SE(3) Levenberg-Marquardt | `lance-graph::splat::registration` | 400 | HIGH | P4 sprint 6-7 | **Queued** | gates on D-SPLAT-2 + D-SPLAT-3 |
| D-SPLAT-6 | `crates/splat-fit` engine — RF/IQ → beamformed → local-maxima → PSF estimate → SH projection → emit Gaussian3D batch | `crates/splat-fit` (new standalone, 0-dep, ndarray-hpc feature) | 1500 | HIGH | P2 sprint 3 | **Queued** | gates on D-SPLAT-1 + D-SPLAT-2 + OQ-SPLAT-3 |
| D-SPLAT-7 | Splat actors — `SplatFitActor`/`PoseAccumulatorActor`/`RegistrationActor`, each owns one `MailboxSoA<Gaussian3D>`; consumes bardioc #17 Rubicon kanban verbatim | `crates/splat-actors` (or `ractor_actors`) | 500 | MED | P3 sprint 4-5 | **Queued** | gates on D-SPLAT-3 + D-SPLAT-6 + bardioc #17 (shipped) |
| D-SPLAT-8 | FMA atlas hydrator — TTL → `fma_class.lance` + `fma_relation.lance` + `fma_atlas_splat.lance` (~150M Gaussians full body) | `lance-graph-ontology` + `crates/fma-hydrator` | 800 | HIGH | P4 sprint 7-8 | **Queued** | gates on OGAR PR #30 Phase 8 + D-SPLAT-3 + ndarray PR #189 (shipped) |
| D-SPLAT-9 | `fma_blueprint::style_recipe` D-Atom catalogue (AnatomicalRegion, OrganSystem, Innervation, Vasculature, Joint, Muscle, Bone, OrganParenchyma, Tract); mirrors PR #433 Odoo pattern | `lance-graph-ontology::fma_blueprint` | 400 | LOW | P4 sprint 7-8 | **Queued** | gates on D-SPLAT-8 |
| D-SPLAT-10 | `memory.ultrasound_frame.lance` + `memory.ultrasound_splat.lance` datasets via `soa_mapping.rs`; new `SensitivityReason::UltrasoundRawPHI`/`UltrasoundAnonymized` variants in `column_mask_bridge` | MedCare-rs `crates/medcare-analytics` | 250 | MED | P5 sprint 9-10 | **Queued** | gates on D-SPLAT-3 + MedCare PR #162 (shipped) |
| D-SPLAT-11 | `commit_event` audit chain for splat ingest via `LanceMembrane::commit_event` (callcenter PR #467, sole-writer membrane); `KnowableFromStore::register("ogit-medcare/ultrasound_ingest", Some(ddl_hint))` | MedCare-rs `crates/medcare-analytics` | 100 | LOW | P5 sprint 9-10 | **Queued** | gates on D-SPLAT-10 + PR #467 (shipped) + OGAR #25/#31 (shipped) |
| D-SPLAT-12 | AR splat renderer — HoloLens OpenXR (clinical AR target) + Cesium ion + Three.js (browser fallback) + headless PNG (regression); CPU does math, GPU only paints | `crates/splat-render` (new) | 1200 | HIGH | P6 sprint 11-13 | **Queued** | gates on D-SPLAT-2 + D-SPLAT-3 + D-SPLAT-5 |
| D-SPLAT-13 | IMU/POSE 4D accumulator — VIO against splat features at IMU rate (~200 Hz); splat-corrected pose at frame rate (~30 Hz); Planning-column readiness at t = −550ms | `splat-actors::PoseAccumulatorActor` | 200 | MED | P3 sprint 4-5 | **Queued** | gates on D-SPLAT-7 |
| D-SPLAT-14 | SaMD documentation track — research-tool → clinical-study → Class IIa (IEC 62366 / IEC 80001 / ISO 14971 / MDR Annex VIII Rule 11). ADR-022 firewall IS the audit-controls evidence base | `q2`/`quarto` or `docs/` | 600 | LOW | P7 sprint 14+ (parallel through P4-P6) | **Queued** | gates on none architecturally; v1/v2/v3 phased |

---

Expand Down
18 changes: 10 additions & 8 deletions .claude/plans/splat-native-ultrasound-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,13 @@ New SoA mapping entry per the existing pattern in `soa_mapping.rs`. Columns:
- `patient_ref: FixedSizeBinary(8)` — anonymized patient ID (per `column_mask_bridge` Hash mode for unauthorized roles)
- `frame_idx: u32` — monotonic frame counter
- `acquisition_ts: TimestampMicros` — frame acquisition time
- `pose_se3: FixedSizeBinary(16)` — packed SE(3) pose (3 translation + 9 rotation matrix, both `f16`)
- `pose_se3: FixedSizeBinary(24)` — packed SE(3) pose (3 translation + 9 rotation matrix, both `f16`; 12 × 2 = 24 B)
- `splat_batch_handle: Binary` — pointer into the splat-volume storage (NOT the splats themselves; those live in `ultrasound_splat.lance` to keep this table queryable)
- `splat_count: u32`
- `mean_amplitude: f32`
- `quality_score: f32` — ICP residual after registration; below threshold ⇒ frame is pruned (kanban col 4)

**Raw RF/IQ stays out of MedCare-rs entirely** — only fitted splats land. PHI is in the splat coordinates + atlas-aligned annotations, not in the raw signal. The `column_mask_bridge` extension adds:
**Raw RF/IQ stays out of MedCare-rs entirely** — only fitted splats land. Per **NR-SPLAT-PHI** (normative rule, see below): scanner-frame splat geometry is non-identifying on its own; the link `patient_ref ↔ splat_volume` in `memory.ultrasound_frame.lance` IS PHI; atlas-aligned anatomical annotations carrying patient-specific landmarks ARE PHI. The `column_mask_bridge` extension adds:

```rust
pub enum SensitivityReason {
Expand All @@ -283,6 +283,8 @@ pub enum SensitivityReason {

**Owner:** `MedCare-rs` (`crates/medcare-analytics`). **~100 LOC + tests.** **Risk: LOW.**

**NR-SPLAT-PHI (normative rule, single source of truth):** *Scanner-frame splat geometry (the `mu`/`sigma_packed` columns; `pose_se3` in scanner frame) is non-identifying on its own. The link between `patient_ref` and a splat volume — i.e. the row in `memory.ultrasound_frame.lance` — IS PHI. Atlas-aligned annotations that name patient-specific landmarks (e.g. `class_id` resolving to "this patient's left biceps brachii at scanner-pose P") are PHI. Raw RF/IQ is PHI by default and is never persisted (it does not enter the MedCare wire). All §3.10 (`column_mask_bridge`) and §7 (risk-matrix HIPAA row) policy decisions cite this rule.*

Every splat ingest writes one `CognitiveEventRow` via `LanceMembrane::commit_event` (callcenter PR #467, the sole-writer membrane). Carries: `actor` (the `SplatFitActor`), `action: "ultrasound_ingest"`, `target_class: "memory.ultrasound_frame"`, `target_row_id`, `version` (the Lance row-version from `KnowableFromStore::register`), `subject` (clinician identity), `consent_evidence` (the patient consent chain). Inner-side; no JSONL audit sink (per MedCare CLAUDE.md Iron Rule 7).

**Tests:** every ingest produces exactly one `CognitiveEventRow`; row-version monotonic; consent-chain non-empty.
Expand Down Expand Up @@ -400,7 +402,7 @@ Three docs:

## 5. Dependencies graph (textual)

```
```text
OGAR #30 Phase 8 (FMA hydrate prep) ─────────┐
ndarray D-SPLAT-2 (SIMD splat ops) ──► D-SPLAT-8 (FMA atlas) ──┐
Expand Down Expand Up @@ -440,7 +442,7 @@ D-SPLAT-4 (SH palette) ───────────────────
| FMA atlas splat pre-computation too large (~5 GB) | MED | Region-on-demand loading (per OQ-SPLAT-X); only the regions matching `class_id` of fitted live splats need to be paged in. Lance versioning + `KnowableFromStore` registry handles the on-demand path. |
| Registration convergence basin too narrow (live ≠ atlas anatomy) | HIGH | Coarse-to-fine multi-resolution ICP (lower SH degree first); patient-specific atlas pre-registration during clinical-study phase. |
| HoloLens OpenXR compute budget exhausted | MED | Render-at-IMU-rate (200 Hz) is not required; render-at-frame-rate (30 Hz) is. Backpressure via the renderer mailbox. |
| HIPAA PHI leak via splat coordinates | HIGH | Splat coordinates are in *scanner frame*, not patient-identifying frame. Patient identity stays in `patient_ref` (Hashed for non-clinical roles per `column_mask_bridge`). |
| HIPAA PHI leak via splat coordinates | MEDIUM | Per **NR-SPLAT-PHI** (§3.10): scanner-frame splat coordinates are non-identifying on their own; PHI lives in the `patient_ref ↔ splat_volume` link and in atlas-aligned annotations carrying patient-specific landmarks. Default `column_mask_bridge` mode for `patient_ref` is `Hash` for non-clinical roles; splat coordinate columns require no masking. |
| SaMD Class IIa technical-file gap | MED | Build the audit-controls evidence chain during P5 (HIPAA wire); the chain IS the certification evidence base. |

---
Expand Down Expand Up @@ -600,7 +602,7 @@ This plan touches **seven repositories**. Each owns a piece of the splat-native
**Critical interconnect:**
- **Raw RF/IQ NEVER enters MedCare storage** — verified by `grep` audit. This is a hard invariant (Iron Rule 7 + Iron Rule from MedCare CLAUDE.md): audit witness stays inner; raw signal stays at the splat-fit boundary; only fitted Gaussians cross into MedCare.
- The MedCare ultrasound dataset is **append-only from the SplatFitActor's perspective** — the actor is the sole writer; MedCare only reads + redacts on query. This preserves the sole-writer membrane discipline from PR #467.
- The patient-identity boundary is `patient_ref` only; splat coordinates are in *scanner frame*, not patient-identifying frame. No PHI in the geometry itself.
- Per **NR-SPLAT-PHI** (§3.10): the patient-identity boundary is the `patient_ref ↔ splat_volume` LINK (Hashed for non-clinical roles); splat coordinates are in *scanner frame* and are not patient-identifying on their own; atlas-aligned annotations carrying patient-specific landmarks ARE PHI and inherit `patient_ref`'s sensitivity class via foreign-key join.

**Sprint commitment:** sprint 9-10 (P5, after FMA atlas + registration land upstream). 1 PR (D-SPLAT-10 + D-SPLAT-11 bundled).

Expand Down Expand Up @@ -656,7 +658,7 @@ This plan touches **seven repositories**. Each owns a piece of the splat-native

## 10.8 The interconnect map (visual)

```
```text
┌──────────────────────────────────────────┐
│ ndarray (D-SPLAT-2) │
│ SIMD substrate: Cholesky, Mahalanobis, │
Expand Down Expand Up @@ -750,11 +752,11 @@ This plan touches **seven repositories**. Each owns a piece of the splat-native

Per the workspace's standing autoattend pattern + lance-graph's two-layer A2A discipline:

**Layer-1 (runtime / code-level):** the `OrchestrationBridge` + `StepDomain` taxonomy already handles cross-domain dispatch. Splat-native adds two `StepDomain` variants:
**Layer-1 (runtime / code-level):** the `OrchestrationBridge` + `StepDomain` taxonomy already handles cross-domain dispatch. The shipped contract (`crates/lance-graph-contract/src/orchestration.rs:37`) defines exactly eight variants: `Crew, Ladybug, N8n, LanceGraph, Ndarray, Smb, Medcare, Kanban`. Splat-native lands as **two new `StepDomain` variants ADDED to that shipped enum** (extending the single-source-of-truth taxonomy, not parallel to it):
- `StepDomain::SplatFit` (the engine consumes RF/IQ → emits SoA)
- `StepDomain::SplatRender` (the renderer consumes SoA → emits pixels)

These compose with existing `StepDomain::{Codec, Thinking, Query, Semantic, Persistence, Inference, Learning}` per OrchestrationBridge contract.
These compose with the existing variants per the `from_step_type` dispatch — splat ingest routes `SplatFit → Ndarray → LanceGraph → Medcare` (fit → SIMD → registration → HIPAA wire); splat render routes `SplatRender ← LanceGraph` (read-only consumer). The two new variants land in the same PR as D-SPLAT-1 (`Gaussian3D` carrier) so the contract stays single source of truth. **Step-type prefixes** for `from_step_type`: `splat_fit.*` → `SplatFit`, `splat_render.*` → `SplatRender`.

**Layer-2 (session / Claude-code-level):** each per-repo doc has a `READ BY:` header naming which session-tier agents bootload it:

Expand Down