clarifier

Failure modes

Every situation clarifier is known to hit, and its exact contract. A situation not listed here that produces a blank canvas or a silent wrong answer is a bug — file it.

SituationContract
CSV fails to parse / not tabular (JSON, prose pasted)Structured error naming what was detected instead (“looks like JSON, not CSV”) — never a partial or garbage render.
Zero usable columns (all id-like / high-cardinality / all-null)Refuses gracefully: “no columns here map to a physical property,” with which columns were seen and why each was excluded. Never an empty canvas with no explanation.
1 row, or all rows identical on every mapped columnSeparation-gain marked insufficient-variance, with its own honest copy (“no variation in the data you mapped”) — distinct from a computed-and-low no-meaningful-gain verdict.
requestAdapter() returns nullCaught; falls to the WebGL2 rung. Never an uncaught rejection.
WebGL2 context creation also failsFalls back to the same Canvas2D renderer the static rung uses — still animated in real time (not the static rung’s single settled frame, a minor difference from SPEC.md’s literal wording, caught and fixed during this build after a version that just showed a blank canvas here).
CSV larger than the resolved point budgetStratified sample down to budget, stated plainly next to the effect. Parsing itself runs on the main thread, not a Web Worker (SPEC.md's stated design, not yet built — measured at 50,000 rows, the app's own largest budget, this is a roughly 180ms pause, not a freeze; see Limitations for the real numbers at other sizes).
Visitor drags a particle mid- or post-settleThe physics-level mechanism (re-inject energy, resume stepping under the same convergence rule) is real and unit-tested (Simulation.perturb()); the pointer-drag UI wiring it to a mouse gesture is not built in v1 — SPEC.md itself frames this as an enhancement layered on top of the required keyboard-operable mapping panel, not a replacement for it, and that required path works today. See Limitations.
Simulation numerically diverges despite the stability clampsHard NaN/Infinity guard every step; on detection, freeze at the last stable frame, log a console warning, show “simulation became unstable — showing the last stable frame.” Never renders NaN positions as a blank or garbage canvas.
Export requested before convergenceAllowed; filename and metadata state the step count and “not yet settled.” Never presented as final silently.
prefers-reduced-motion toggled mid-sessionA live matchMedia listener switches to the static rung immediately, not only on next load.
Dataset exceeds the separation-gain sample cap (2,000 rows)The metric is computed on a stratified sample of up to 2,000 rows, same sampling rule as the render budget; stated on-page next to the numbers. Documented in Limitations — not in SPEC.md, added because exact silhouette is O(n²) and would stall the main thread on a 50,000-row dataset.