Skip to content

docs: SpatialData tables integration roadmap#522

Open
xinaesthete wants to merge 2 commits into
mainfrom
claude/eager-hamilton-68eeba
Open

docs: SpatialData tables integration roadmap#522
xinaesthete wants to merge 2 commits into
mainfrom
claude/eager-hamilton-68eeba

Conversation

@xinaesthete

@xinaesthete xinaesthete commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

What this is

An exploratory design-doc set (research/scoping, not decisions — decisions still belong in docs/adr/) for making SpatialData tables first-class in MDV. Added under docs/design/spatial-tables/. No code changes.

Every claim is anchored to file:line, from a codebase deep-dive across MDV plus the sibling libraries SpatialData.ts, anndata.js, and codecs/tgpu-htj2k.

The core deliverable (doc 00)

A spatial element (shapes / labels) annotated by a table should get a first-class association to that table's rows — so geometry is coloured, filtered, highlighted, and tooltipped by table columns, and filtering flows both ways with every other chart on that table.

Key findings:

  • MDV already does this for the points representation (shared DataStore row-index space: filterArray + getColorFunction + dataHighlighted). Shapes/labels do not — the association is a bare stub (table_association.ts type with no resolver), fillColorByColumn UI is commented out, shapes render static and ignore the filter.
  • SpatialData.ts already ships the join layer (tableAssociations.ts: loadFeatureRowIndexByFeatureIndex → Int32Array, per-feature featureState colouring, pick events carrying rowIndex) — but it joins on instance_key against the untouched zarr store.
  • The current convert-spatial path destroys that identity: instance_key is discarded (_instance_key at conversion.py:373/:639) and the single anndata.concat row-stacks tables, scrambling order. So the core feature and the read-path / converter rework are one project, not separate ones.

The enabling / adjacent themes

Doc Topic
00-table-element-association.md The deliverable — first-class shapes/labels ↔ table association
01-js-dataloader.md A zarr-read DataLoader (spatialdata.js/anndata.js) coexisting with h5 writes via a backing hint
02-convert-spatial-tables.md Flexible table grouping (stop force-merging grid + cells); preserve instance_key/region_key
03-shared-spatial-contexts.md Project-scoped SpatialData store/image cache (follows the queryClient precedent)
04-views-datasources-chartmanager.md Datasource picker, cross-DS layout, new view kind, runtime ChartManager swap
05-dimension-async-filtering.md Async/GPU/WASM filtering + untangling useFilteredIndices
README.md Overview, load-bearing facts, risk/reward, suggested sequencing

For reviewers

  • Docs only — nothing is wired or decided; this is a map to support scoping conversations.
  • The one decision flagged for the maintainer before implementation: the delivery route for row identity — a minimal read-only JS table loader against the shared store vs. fixing the converter to preserve instance_key/region_key. Doc 00 lays out both.
  • One item to verify against installed @spatialdata/*@0.2.5 packages (absent in the worktree): whether @spatialdata/vis exposes per-layer featureState injection (Option A in doc 00).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Added a spatial tables design roadmap covering first-class table-to-geometry association, browser-side column loading, and conversion grouping strategies.
    • Introduced design notes for sharing spatial data/image context to reduce redundant loads.
    • Documented proposed directions for evolving views, datasource orchestration, and ChartManager behavior.
    • Added a plan to make spatial table dimension filtering asynchronous (GPU/WASM/worker-based) and to untangle main-thread filtering work.

Research/scoping design docs (exploratory, not decisions) for making
SpatialData `tables` first-class in MDV. Grounded with file:line anchors
from a codebase deep-dive across MDV, SpatialData.ts, anndata.js, and
tgpu-htj2k.

Centered on the core deliverable (doc 00): spatial elements (shapes/labels)
annotated by a table should be coloured/filtered/highlighted by table
columns, reusing the points path's shared DataStore row-index space. Covers
the enabling/adjacent themes: a JS zarr-read DataLoader with h5 write
routing, flexible convert-spatial table grouping, project-scoped shared
spatial contexts, views/DataSources/ChartManager rework, and async/GPU/WASM
filtering.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploy Preview for mdv-dev ready!

Name Link
🔨 Latest commit d156c85
🔍 Latest deploy log https://app.netlify.com/projects/mdv-dev/deploys/6a4e24a9ff1dc20008ea9b46
😎 Deploy Preview https://deploy-preview-522--mdv-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 50f6a214-9f5d-4382-bfa5-92b0740ffd0f

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6fe24 and d156c85.

📒 Files selected for processing (5)
  • docs/design/spatial-tables/02-convert-spatial-tables.md
  • docs/design/spatial-tables/03-shared-spatial-contexts.md
  • docs/design/spatial-tables/04-views-datasources-chartmanager.md
  • docs/design/spatial-tables/05-dimension-async-filtering.md
  • docs/design/spatial-tables/README.md
✅ Files skipped from review due to trivial changes (4)
  • docs/design/spatial-tables/03-shared-spatial-contexts.md
  • docs/design/spatial-tables/README.md
  • docs/design/spatial-tables/04-views-datasources-chartmanager.md
  • docs/design/spatial-tables/02-convert-spatial-tables.md

📝 Walkthrough

Walkthrough

Adds seven new design documents under docs/design/spatial-tables/ plus a roadmap README. The docs describe table-element association, a JS DataLoader, convert-spatial grouping, shared spatial contexts, ChartManager/view changes, and async filtering.

Changes

Spatial Tables Design Documentation

Layer / File(s) Summary
Roadmap README
docs/design/spatial-tables/README.md
Indexes the theme docs, defines the core deliverable, and outlines load-bearing facts, risks, and sequencing.
Table-element association design
docs/design/spatial-tables/00-table-element-association.md
Defines the association goal, current gaps, rendering options, bidirectional filtering, phasing, and open questions.
JS DataLoader design
docs/design/spatial-tables/01-js-dataloader.md
Describes a zarr-backed JS DataLoader, SAB encoding rules, hybrid h5/zarr routing, and rollout risks.
Convert-spatial table grouping design
docs/design/spatial-tables/02-convert-spatial-tables.md
Documents current table flattening issues and proposes grouping-based conversion changes.
Shared spatial contexts design
docs/design/spatial-tables/03-shared-spatial-contexts.md
Describes duplicated store/image fetching and a project-scoped shared cache path.
Views/DataSources/ChartManager design
docs/design/spatial-tables/04-views-datasources-chartmanager.md
Outlines datasource picker, shared layout, custom view kind, and runtime ChartManager swap proposals.
Async dimension filtering design
docs/design/spatial-tables/05-dimension-async-filtering.md
Analyzes the synchronous filter model and proposes GPU/WASM async filtering plus useFilteredIndices simplification.

Estimated code review effort: 2 (Simple) | ~12 minutes

Possibly related PRs

  • Taylor-CCB-Group/MDV#279: Region-aware convert_spatialdata_to_mdv changes align with the new spatial-table grouping and region/element mapping design.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the PR’s docs-only SpatialData tables integration roadmap.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/eager-hamilton-68eeba

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
docs/design/spatial-tables/02-convert-spatial-tables.md (1)

103-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pick one actual default grouping policy.

The recommendation currently says default to merge-by-region or one-per-table, which are materially different behaviors. Please choose one as the default and relegate the other to an explicit fallback/compatibility mode so downstream docs and implementation don’t diverge.

🤖 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 `@docs/design/spatial-tables/02-convert-spatial-tables.md` around lines 103 -
105, The default grouping policy is ambiguous in the spatial tables design doc,
since “merge-by-region” and “one-per-table” are both presented as defaults.
Update the recommendation to choose one actual default in the relevant section
of the document, and move the other option into an explicit compatibility or
fallback mode so the intended behavior is unambiguous for downstream
implementation and docs.
🤖 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 `@docs/design/spatial-tables/03-shared-spatial-contexts.md`:
- Around line 24-29: The fenced example in the shared spatial contexts doc is
missing a language tag, which triggers markdownlint MD040. Update the code fence
around the QueryClientProvider/ChartManagerProvider snippet to include an
appropriate language identifier so the example renders consistently and passes
the docs lint checks.
- Around line 65-74: The shared spatial store cache proposal can permanently
poison future loads if it stores raw rejected `readZarr` promises. Update the
cache logic in `spatial_store_cache.ts` and the wrapper around
`SpatialDataProvider`/`createMdvPortal` so failed loads are evicted on
rejection, or split the inflight promise tracking from the fulfilled
`SpatialData` entry. Keep the deduplication behavior for successful loads keyed
by resolved store URL (and `selection` if applicable), but ensure transient
errors can be retried instead of reusing a rejected promise.

In `@docs/design/spatial-tables/04-views-datasources-chartmanager.md`:
- Around line 89-120: The view schema change in the ChartManager/ViewManager
flow introduces a new discriminator and moves layout state from per-datasource
to region-level, so add a backward-compatible migration/defaulting path for
existing saved views. Update the relevant branches in `_init(view)`,
`getState()`, and `ViewManager.hasUnsavedChanges` to recognize both legacy and
new shapes, normalize older `views.json` entries on load, and ensure the
save/compare logic does not mark unchanged migrated views as dirty forever.

In `@docs/design/spatial-tables/05-dimension-async-filtering.md`:
- Around line 74-76: The sibling repo reference in the design doc uses a
machine-local absolute path, which is not portable. Replace the
`/Users/ptodd/code/codecs/tgpu-htj2k` wording in the affected markdown with a
repo-relative reference or a neutral placeholder, keeping the rest of the
guidance about WebGPU/TypeGPU/TGSL and import-by-path intact.
- Around line 8-13: Clarify the description of the synchronous filtering path in
the spatial-tables design doc: the current wording overstates that all
filter-derived work is already off-thread, but chart-scope predicate/ownership
handling still runs on the main thread. Update the affected section to
distinguish the remaining synchronous layer from the off-thread worker/SAB-based
derivations, and adjust the phrasing around the filter-evaluation hotspot so it
accurately reflects what is still main-thread work. Refer to the “Filter
evaluation” section and the “everything derived from a filter” sentence when
revising the wording.
- Around line 45-70: Clarify the documentation so `useFilteredIndices()` is not
described as the shared worker-computed compact index list when that role is
actually implemented by `useSimplerFilteredIndices`. Update this section to
separate the two responsibilities: `useSimplerFilteredIndices` for the cached
async worker result, and `useFilteredIndices` for the higher-level hook that
also applies the synchronous chart-scope predicate. Keep the wording aligned
with the symbols `useFilteredIndices`, `useSimplerFilteredIndices`, and
`useChartScopeFilterPredicate` so the ownership of each filtering path is
unambiguous.

In `@docs/design/spatial-tables/README.md`:
- Around line 84-108: The ASCII diagram fence in the spatial-tables README is
missing a language tag, which triggers markdownlint in this repo. Update the
fenced block around the diagram to use the text language label so the docs stay
lint-clean; the change is isolated to the README diagram block.

---

Nitpick comments:
In `@docs/design/spatial-tables/02-convert-spatial-tables.md`:
- Around line 103-105: The default grouping policy is ambiguous in the spatial
tables design doc, since “merge-by-region” and “one-per-table” are both
presented as defaults. Update the recommendation to choose one actual default in
the relevant section of the document, and move the other option into an explicit
compatibility or fallback mode so the intended behavior is unambiguous for
downstream implementation and docs.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d13147ec-4291-487e-ab80-15b1fa80baa2

📥 Commits

Reviewing files that changed from the base of the PR and between 7553aa5 and 8d6fe24.

📒 Files selected for processing (7)
  • docs/design/spatial-tables/00-table-element-association.md
  • docs/design/spatial-tables/01-js-dataloader.md
  • docs/design/spatial-tables/02-convert-spatial-tables.md
  • docs/design/spatial-tables/03-shared-spatial-contexts.md
  • docs/design/spatial-tables/04-views-datasources-chartmanager.md
  • docs/design/spatial-tables/05-dimension-async-filtering.md
  • docs/design/spatial-tables/README.md

Comment thread docs/design/spatial-tables/03-shared-spatial-contexts.md Outdated
Comment on lines +65 to +74
- New module `src/react/spatialdata/spatial_store_cache.ts`: a `Map<string, Promise<SpatialData>>`
keyed by resolved store URL (+ `selection` if ever used). One instance per project — a module
singleton mirroring `queryClient`, or hung off `chartManager` / `ProjectContext`.
- Add a thin `SpatialStoreProvider` inside `createMdvPortal`
([react_utils.tsx:108](../../../src/react/react_utils.tsx)), alongside `ProjectProvider`,
exposing the cache.
- Replace the two direct `<SpatialDataProvider source>` usages with a wrapper that resolves the
store from the shared cache (or wrap upstream `SpatialDataProvider` so its `readZarr` is deduped
by URL). Result: N charts + dialogs on one path → **1 `readZarr`, 1 `SpatialData` instance,
shared `parquetTableCache` etc.** (layers (a) and (b) shared) with minimal surface area.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Don't cache rejected readZarr promises indefinitely.

A failed load would poison this cache if you store the raw promise here; subsequent reads for the same URL would keep failing until eviction or process restart. Please evict on rejection, or separate the inflight promise from the fulfilled SpatialData entry, so transient store errors remain recoverable.

🤖 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 `@docs/design/spatial-tables/03-shared-spatial-contexts.md` around lines 65 -
74, The shared spatial store cache proposal can permanently poison future loads
if it stores raw rejected `readZarr` promises. Update the cache logic in
`spatial_store_cache.ts` and the wrapper around
`SpatialDataProvider`/`createMdvPortal` so failed loads are evicted on
rejection, or split the inflight promise tracking from the fulfilled
`SpatialData` entry. Keep the deduplication behavior for successful loads keyed
by resolved store URL (and `selection` if applicable), but ensure transient
errors can be retried instead of reusing a rejected promise.

Comment thread docs/design/spatial-tables/04-views-datasources-chartmanager.md
Comment thread docs/design/spatial-tables/05-dimension-async-filtering.md Outdated
Comment thread docs/design/spatial-tables/05-dimension-async-filtering.md
Comment thread docs/design/spatial-tables/05-dimension-async-filtering.md Outdated
Comment thread docs/design/spatial-tables/README.md Outdated
- tgpu-htj2k: replace machine-local absolute path with the public repo
  URL (https://github.com/xinaesthete/tgpu-htj2k, may be renamed)
- 05: don't overstate async coverage — chart-scope predicate and
  cross-chart ownership still run on the main thread; clarify that the
  shared worker-computed compact list is useSimplerFilteredIndices and
  useFilteredIndices layers the sync chart-scope predicate on top
- 04: note the region-scoped layout is an additive/opt-in View schema
  change (kind discriminator), not a migration; getState()/hasUnsavedChanges
  must dispatch on the discriminator (per @xinaesthete)
- 03: caution against caching rejected readZarr promises indefinitely;
  evict on rejection or separate inflight from fulfilled
- label fenced code blocks (MD040): tsx / text

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@xinaesthete

Copy link
Copy Markdown
Collaborator Author

Addressed the review in d156c85 (docs-only):

  • tgpu-htj2k path — swapped the machine-local absolute path for the now-public repo URL (https://github.com/xinaesthete/tgpu-htj2k, noted as liable to rename).
  • 05 async overstatement — clarified that the chart-scope predicate and cross-chart ownership still run on the main thread, and that the shared worker-computed compact list is useSimplerFilteredIndices while useFilteredIndices layers the synchronous chart-scope predicate on top.
  • 04 view schema (per @xinaesthete) — added a note that the region-scoped layout is an additive/opt-in View change via a kind/schemaVersion discriminator, not a migration: old per-datasource views keep their shape and code path, new views take the discriminated branch, so the real requirement is getState()/hasUnsavedChanges dispatching on the discriminator (no load-time migration).
  • 03 store cache — added a caveat not to cache rejected readZarr promises indefinitely (evict on rejection, or keep inflight separate from fulfilled).
  • MD040 — labelled the fenced blocks (tsx/text).

Still exploratory research, not decisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant