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
33 changes: 29 additions & 4 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ A reserved **Stack Entry** that names ordered children for future blending or ag
_Avoid_: framebuffer layer until the rendering behavior exists

**Resource Resolver**:
The store-agnostic boundary that turns structural **Render Stack** inputs into stable loaded resources for renderers.
_Avoid_: viewer-local cache, periodic snapshotter
The store-agnostic **and renderer-agnostic** boundary that turns structural **Render Stack** inputs into stable loaded resources for renderers. Owns the cache, request supersession, cancellation, streaming partials, eviction, and world bounds. Lives in `@spatialdata/core`: it depends on neither deck.gl nor React, and is consumed by every **Renderer Adapter** ([ADR 0004](docs/adr/0004-resource-resolver-owned-by-core.md)).
_Avoid_: viewer-local cache, periodic snapshotter, deck-coupled engine

**Renderer Adapter**:
The code that turns resolved resources plus entry props into Viv/deck layer instances.
_Avoid_: state store
The code that turns resolved resources plus entry props into renderer output — deck.gl `Layer`s, Viv image props, or a three.js/WebGPU pass. Pure and synchronous: it is handed resolved state and cannot start a load.
_Avoid_: state store, "the renderer" (there is more than one)

**Runtime Attachment**:
An unsaved function or object supplied by the host application alongside a **Render Stack**, such as `hostLayerResolver`, `onFeatureHover`, `onFeatureClick`, raw deck handlers, DOM portal targets, or deck layer factories.
Expand Down Expand Up @@ -60,10 +60,35 @@ _Avoid_: calling the points tooltip a "feature tooltip"; pulling tooltip values
Transient, interactive emphasis of *all* points belonging to one chosen **Points Feature** (e.g. hovering a gene in the catalog panel brightens its points and de-emphasises the rest). It is ephemeral **runtime** state — a cheap recolor/re-emphasis of the already-resident batch with **no reload or refilter** — not part of the serializable colour encoding or **Render Stack** config. Distinct from deck.gl `autoHighlight`, which emphasises a single *picked point*, not a whole feature.
_Avoid_: persisting highlight into the Stack Entry; conflating with per-object `autoHighlight`; reloading geometry on highlight change

**Resolution**:
The state of one loaded resource of a **Spatial Entry**, as a value: `idle | loading | ready | failed`. `loading` carries `partial` (what *this* load has produced so far — the streaming scan's growing buffer) and `stale` (the last good value from the *previous* load). `failed` may also carry `stale`. Resolutions are **per-resource, not per-entry** — a shapes entry with a broken tooltip column must still draw its geometry.

`stale` is a **retention, not a guarantee**: *while it is retained*, a failed or in-flight refine keeps drawing rather than blanking. It is released on eviction and on non-retryable failure (see **Resource Ceiling**), after which the resource is simply not renderable and the UI shows the **Spatial Entry Error** instead. Callers must handle the no-stale case; they may not assume a previously-ready resource stays drawable.
_Avoid_: a status enum beside a value field; a tri-state (`undefined | null | T`) plus a `loaded` boolean; a per-entry `Result`; treating `stale` as a permanent fallback

**Spatial Entry Error**:
A structured, typed **domain failure** of a resource — not an exception, not a missing layer, not a `console.error`. Every case carries what the UI needs to explain itself (`coordinate-system-not-found` carries `availableCoordinateSystems`; `points-preload-too-large` carries `rowCount` and `maxRows`) plus a `retryable` flag that gates a Retry affordance. `retryable` — not the union — is what prevents a failed scan settling permanently.
_Avoid_: a bare `Error`; swallowing into `console.error`; modelling *absence* as failure (a points element with no `feature_key` is `ready(null)`, a settled fact, not a failure)

**Entry Notice**:
A non-fatal domain fact about a **successfully** resolved entry — preload truncated, selection served from memory, catalog is a resident-subset preview, image channel defaults fell back. A channel distinct from **Spatial Entry Error**, so healthy data never renders as an error.
_Avoid_: overloading the failure channel; a `degraded` resolution status

**Encoded Tier / Decoded Tier**:
The two forms a loaded payload takes: compressed bytes as fetched (parquet file or row-group bytes; a zarr chunk before its codec), and the materialized form (an Arrow table; a typed-array chunk). Both ingest paths — zarr and parquet — have both tiers, and a cache may be bounded independently at each. Dropping a **Decoded Tier** entry while retaining its **Encoded Tier** trades memory for a re-decode; the trade is only sound where decode is off the main thread ([ADR 0005](docs/adr/0005-memory-accounting-before-management.md)).
_Avoid_: "the cache" (there are four); conflating a parquet whole-file cache with a per-chunk one

**Resource Ceiling**:
The byte bound a working set must fit. When it would be exceeded, the policy is *degrade to fit* — coarsen, cap, or evict — not crash and not silent truncation. Distinct from the current points **memory cap**, which is a row count with no accounting behind it. Deferred until an actual out-of-memory case can be provoked; measurement comes first ([ADR 0005](docs/adr/0005-memory-accounting-before-management.md)).
_Avoid_: quota, limit, budget-as-a-guess

## Relationships

- A **Render Stack** contains zero or more ordered **Stack Entries**.
- A **Spatial Entry** is resolved by a **Resource Resolver** before a **Renderer Adapter** creates Viv/deck output.
- A **Spatial Entry**'s resources are each held as a **Resolution**; a failed one carries a **Spatial Entry Error**, a successful one may carry **Entry Notices**.
- A **Resource Resolver** is shared across **Renderer Adapters** (deck.gl, Viv, three.js/WebGPU, headless); it knows about none of them.
- A cached payload exists in an **Encoded Tier**, a **Decoded Tier**, or both; a **Resource Ceiling** bounds them.
- A **Host Overlay** is saved as a descriptor and materialized by the host application at runtime.
- A **Group Entry** may order child entries, but does not yet imply framebuffer or blending behavior.
- A **Runtime Attachment** may observe or materialize stack entries, but is not part of saved **Render Stack** config.
Expand Down
9 changes: 8 additions & 1 deletion docs/adr/0001-render-stack-owned-by-layers.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Render Stack Owned By Layers

The canonical ordered render description lives in `@spatialdata/layers` as `RenderStack`, while `@spatialdata/vis` adapts that stack into React, Viv, and deck.gl rendering. Host overlays are saved as descriptors and resolved by the host application at runtime, so MDV can interleave scatter, gates, selections, and SpatialData entries without storing raw deck layer instances or reintroducing parallel `layerOrder` / `stackOrder` state.
> **Amended by [ADR 0004](0004-resource-resolver-owned-by-core.md) (2026-07-14).**
> The **package-placement** claim below is superseded: `RenderStack` lives in
> `@spatialdata/core`, not `@spatialdata/layers`. The **Resource Resolver** takes a
> Render Stack as input and is renderer-agnostic, so dependency direction forces the
> move. Everything else in this ADR stands unchanged — host overlays as descriptors,
> no parallel `layerOrder` state, MobX outside the contract.

The canonical ordered render description lives in `@spatialdata/layers` as `RenderStack` *(**historical** — superseded by ADR 0004: it lives in `@spatialdata/core`; `layers` and `vis` retain re-exports as compatibility shims)*, while `@spatialdata/vis` adapts that stack into React, Viv, and deck.gl rendering. Host overlays are saved as descriptors and resolved by the host application at runtime, so MDV can interleave scatter, gates, selections, and SpatialData entries without storing raw deck layer instances or reintroducing parallel `layerOrder` / `stackOrder` state.

MobX may be used by MDV-facing control UI, but it is not part of the `@spatialdata/layers` contract or the default renderer API. MobX-controlled panels should be explicit control islands, especially while adopting React Compiler, because observable direct editing and automatic memoization have different assumptions.
Loading
Loading