medOS ultra

SNOMED Body-Map Kit

Reusable, pluggable, real-time anatomical annotation surface keyed to SNOMED body sites.

5 min read diagramsUpdated 2026-05-30docs/architecture/snomed-body-map-kit.md

Status (2026-05-30): core shipped + sandbox/Node-verified; Supabase realtime adapter + the 2D/dental mappers + full host extraction are staged (designed here). Lives in web/packages/medical-kit/src/body-model-3d/ (@medical-kit/body-model-3d).

The 3D SNOMED body annotator is not one screen — it’s a kit: a shared marker store, a marker-source registry, a pluggable body-part-mapper contract, a Canvas viewer, a still-image poster facade, and a pluggable realtime transport. Any app surface (ClinicWorkspace, Chief Complaints + AI, Screening modal, OR Record + packing/surgical, …) ties into the same markers and drops/reads pins without prop-drilling.

1. What the kit exports

Export Purpose
BodyModelViewer R3F Canvas: GLB body, drei `` markers (free placement via position/onAnnotatePoint), occlusion, focusPosition camera fly, onPosterCapture, demand frameloop.
BodyModelPoster + MarkerOverlay Freeze-to-still facade: still image + JS-projected interactive DOM markers; boots WebGL only on drag. Fail-safe, flag-gated.
useBodyAnnotation (Zustand) The shared marker store — markers, selectedId, add/update/remove/select/setAll/clear. The tie-in backbone.
registerMarkerSource / getMarkerSource Source registry — a form declares itself so its markers get a label/colour and re-open its values on select.
connectRealtime / RealtimeAdapter Pluggable transport — local marker changes publish to peers, remote snapshots flow into the store (echo-guarded). Supabase slots in here.
registerBodyPartMapper / BodyPartMapper The mapper contract — 3D SNOMED today; 2D body diagram / dental odontogram / eye-map are future mappers satisfying the same contract.
COORDINATE_SETS, resolveAnatomyCoding, getRegionCodings SNOMED/FMA/PCS region coding (deployment-aware).

2. The pluggable mapper (BodyPartMapper)

A mapper is any anatomy surface. The host renders the active mapper; the store + sources are mapper-agnostic, so a marker dropped by a form pops up on whichever mapper is mounted.

interface BodyPartMapper {
  id: string;            // 'snomed-3d' | 'body-2d' | 'dental' | …
  label: string;
  codeSystem: string;    // 'http://snomed.info/sct'
  domain: string[];      // ['body'] | ['teeth'] | ['eye'] — routes a marker to the right mapper
  render: (p: BodyMapperRenderProps) => ReactNode;   // 3D canvas / 2D svg / chart
  search?: (q: string) => { regionId; label; code? }[];
  views?: string[];      // 'orbit' | 'front' | 'back' | 'left' | 'right' | 'quad'
}

The 3D SNOMED mapper wraps BodyModelViewer. A future dental mapper renders an odontogram from the same store; mapperForDomain('teeth') routes a tooth marker to it. No 2D mapper pulls in R3F — each provides its own renderer.

3. Integration contract — how the named surfaces tie in

Every surface uses the same two calls. No props, no provider.

// once, at module load:
registerMarkerSource({ id: 'or-record', label: 'OR record', color: '#e74c3c', glyph: '🧽' });

// Chief Complaints + AI — AI extracts complaint → drops a pin:
useBodyAnnotation.getState().add({
  regionId: 'chest', nodeName: 'Chest', kind: 'complaint', type: 'warning', sev: 'moderate',
  source: 'complaint', payload: { complaint: 'Sharp chest pain', onset: '2h' },
});

// OR Record modal (packing / surgical counts) — count incorrect → red pin pops up:
useBodyAnnotation.getState().add({
  regionId: 'abdomen', kind: 'surgical', type: 'critical', sev: 'critical',
  source: 'or-record', payload: { item: 'Raytec', in: 5, out: 4, status: 'INCORRECT' },
});

// Screening modal — a positive finding pin; ClinicWorkspace mounts the viewer + reads selection.
const selected = useBodyAnnotation((s) => s.markers.find((m) => m.id === s.selectedId));
Surface What it pushes What it reads
ClinicWorkspace hosts the mapper; arms tools selectedId → opens the right modal for the pin’s source
Chief Complaints + AI AI-extracted complaint pins (source: 'complaint') existing complaint pins to refine
Screening modal positive findings as pins findings by region
OR Record (packing/surgical) packing + count pins (source: 'or-record') retained-item / incorrect-count pins

Selecting a sourced marker re-opens that form’s values in the detail panel — verified live (a Chief-Complaint drop surfaced LINKED FORM · Chief complaint · "Sharp chest pain…").

4. Realtime via Supabase (pop-up across clients)

connectRealtime(adapter) makes the store transport-agnostic. A Supabase adapter:

function supabaseBodyAnnotations(supabase, encounterId): RealtimeAdapter {
  const ch = supabase.channel(`body:${encounterId}`);
  return {
    publish: (markers) => ch.send({ type: 'broadcast', event: 'markers', payload: markers }),
    subscribe: (apply) => {
      ch.on('broadcast', { event: 'markers' }, ({ payload }) => apply(payload)).subscribe();
      // hydrate from the table on connect, then live:
      supabase.from('body_annotations').select('*').eq('encounter_id', encounterId)
        .then(({ data }) => data && apply(data.map(rowToMarker)));
      return () => supabase.removeChannel(ch);
    },
  };
}

Read-model rule (web/CLAUDE.md): the frontend never writes a read-model table directly. Two compliant paths:

  • Collaborative layer (like the dynamic-sheet presence/edit-lock pattern): a dedicated body_annotations table with RLS scoping rows to the encounter + author, frontend writes allowed (it’s collaboration data, not the clinical write-truth), realtime broadcast for live pop-up. Promote a pin to a clinical record (Condition/Observation/Procedure) through the backend write path when signed.
  • Mirror-only: markers authored via backend APIs → projected to body_annotations → realtime → store (publish omitted; read-only mirror).

body_annotations sketch: id, encounter_id, region_id, kind, type, severity, note, position jsonb, source, payload jsonb, history jsonb, author_id, created_at. Echo-guarded in the store so an applied remote snapshot never re-publishes.

5. Miniapp / embedding

The kit is import-only — a miniapp mounts `` (or the viewer) + the panels it needs, calls connectRealtime(supabaseBodyAnnotations(...)) once, and registerMarkerSource for its forms. The sandbox target BodyModelAnnotationStudio is the reference host (tool dock, dark marker list, deep-values detail, pain scale/tracker, MSK + region-aware special tests, OR counts, device LDA forms, search→zoom, single/split/quad views, linked-forms, timeline scrubber).

6. Verified vs staged

  • Shipped + verified: store CRUD + source registry (Node); realtime publish/apply + echo guard (Node); mapper registry + domain routing (Node); free placement, camera-focus, poster facade (type-clean); the full studio incl. linked-form pop-up (live: 4→5 + payload), single/split/quad (4 canvases), all panels (DOM, no error boundary).
  • Staged: the Supabase adapter + body_annotations migration + RLS; the 2D/dental mappers; extracting the studio chrome into a reusable ``; wiring the four real app surfaces; pin↔card dashed leader line. Note: WebGL render + screenshots couldn’t be verified in the sandbox preview (1px pane / flaky GL) — verify the live 3D in a real browser.
Ask Anything