<!-- Logo: assets/met-al_logo.png -->
<p align="center">
  <img src="../assets/met-al_logo.png" alt="MET-AL" width="360" />
</p>

# MET-AL — Experiment Tracker

Each experiment is an **isolated prototype** living on its own branch + worktree under
`.claude/worktrees/experiment-<name>/`. It explores one of the themes/ideas from
[`../ideas.html`](../ideas.html). Experiments are intentionally self-contained — duplicate
scaffolding across them is expected and consolidated on the `integration` branch.

## Conventions

- **Branch:** `experiment/<name>` · **Worktree:** `.claude/worktrees/experiment-<name>/`
- **Each experiment contains:** `PLAN.md`, the runnable prototype, and `REVIEW.md`.
- **Status:** `planned` → `building` → `reviewed` → `done` / `parked`.

## Running a prototype

All prototypes are ES-module apps — serve them over HTTP (they do **not** run by
double-clicking `index.html` in Chrome/Safari — module CORS over `file://`):

```
python3 -m http.server 8000 --directory .
# open http://127.0.0.1:8000/.claude/worktrees/experiment-<name>/index.html
```

The `integration` branch ships **single-file `dist/*.html` builds** that *are* double-clickable.

## Experiments

| # | Theme / idea | Name | Branch | Status | Verdict | One-liner |
|---|---|------|--------|--------|---------|-----------|
| 01 | T01 Client-side wrapper | `client-side-wrapper` | `experiment/client-side-wrapper` | reviewed | ✅ merge | In-browser categorical+continuous stats, fidelity table, future-primitives panel |
| 02 | T02 Modern interaction | `stat-interaction` | `experiment/stat-interaction` | reviewed | ✅ merge | Metric/Dimension/Filter/Facet explorer, 3 linked views — **+partial-sums fix** |
| 03 | T03 Novel plotting | `novel-plotting` | `experiment/novel-plotting` | reviewed | ✅ merge | Performance · Reliability+Brier · Threshold scrubber · MODE-style objects |
| 04 | T04 Modernization | `modernization` | `experiment/modernization` | reviewed | ✅ merge | Guided journey + provenance + shareable URL + rule-based narration |
| 05 | T03 Spatial maps | `spatial-maps` | `experiment/spatial-maps` | reviewed | ✅ merge | Map-native error field, region masks, masked MET stats, hover/brush |
| 06 | T03 Ensemble | `ensemble-verification` | `experiment/ensemble-verification` | reviewed | ✅ merge | Rank histogram · spread-skill · reliability+Brier · CRPS, dispersion knob |
| 07 | T03 3D / volumetric | `volumetric-3d` | `experiment/volumetric-3d` | reviewed | ✅ merge | Orbitable 2.5D metric cube (lead×threshold×region) with slice scrubbing |

**Round 1** (themes 01–04) and **Round 2** (Theme-02 fix + experiments 05–07) all built,
MET-math-verified, and browser-verified — **7/7 merge**. Round-2's experiments 05–07 each bake
in the round-1 **ratio-of-sums** aggregation lesson and surface the wrong mean-of-ratios on screen.

## Consolidation (`integration` branch) — ✅ done

All seven experiments are gathered under [`../apps/`](../apps/), a shared verification-math
library is extracted to [`../lib/met-stats.mjs`](../lib/met-stats.mjs) (**129/129** self-test via
`node lib/selftest.mjs`). **All seven apps now share the lib** — the original four
(client-side-wrapper, novel-plotting, spatial-maps, volumetric-3d) plus a later pass migrating the
three formerly-standalone apps (ensemble-verification RHIST/CRPS/spread-skill/reliability,
modernization PSTD reliability/Brier, stat-interaction's continuous path), proven non-regressive by
~34k differential + real-data assertions. Only stat-interaction's *categorical* path stays local by
design. Double-clickable single-file builds ship in [`../dist/`](../dist/) via
[`../tools/inline.mjs`](../tools/inline.mjs), and a branded gallery lands at
[`../index.html`](../index.html). Logo trimmed (app copies 230 KB → 51 KB): **dist total
4.21 MB → 2.68 MB (−36%)**, no rendered numbers changed.

## Round 3 — real-data ingestion (`stat-ingest`) — ✅ built

The keystone for wiring **real** MET output into the apps: a header-driven, version-tolerant parser
[`../lib/met-stat-parse.mjs`](../lib/met-stat-parse.mjs) (**97/97** self-test via
`node lib/met-stat-parse.selftest.mjs`) for `.stat` / per-type `.txt` / MODE files, covering CTC,
CTS, SL1L2, CNT, PCT, PSTD, PRC, RHIST, ECNT, ORANK + vector wind VL1L2/VAL1L2/VCNT, feeding the
consolidated `met-stats.mjs`. **Validated on real MET v12.2 output:** all 1452 `.stat` files parse with
0 errors; 7453/7453 SL1L2→CNT and 1586/1586 VL1L2→VCNT independent-oracle cross-checks pass.
A runnable demo app [`../apps/stat-ingest/`](../apps/stat-ingest/) (build:
[`../dist/stat-ingest.html`](../dist/stat-ingest.html)) parses a synthetic-but-byte-shaped sample —
**paste or drop a real `.stat` file to replace it** — and renders every ingested value via
`textContent` (the deferred injection-safe ingestion-boundary hardening). End-to-end correctness is
proven by parsing PCT bins and reproducing the paired PSTD line's pre-computed Brier decomposition
exactly (browser-verified). Schemas confirmed against the MET User's Guide (met.readthedocs.io).

- **Start here:** [`../index.html`](../index.html) — the branded gallery (served + single-file links per app).
- **Single-file builds:** [`../dist/*.html`](../dist/) — double-clickable, offline, self-contained.
- **Shared math:** [`../lib/met-stats.mjs`](../lib/met-stats.mjs) · builder [`../tools/inline.mjs`](../tools/inline.mjs).
- **Full write-up:** [`../CONSOLIDATION.md`](../CONSOLIDATION.md) — lib API, inliner, dist, de-dup decisions, trimming.

Repo-wide at consolidation: `node --check` 81/81; one `http(s)://` scan over shipped code (excl.
base64 + markdown) ⇒ 0 real external deps (only `//` comments + the W3C SVG namespace URI); all 7
`dist/*.html` self-contained. See also [`reports/EXPERIMENT_REVIEW.md`](reports/EXPERIMENT_REVIEW.md)
and [`reports/screenshots/`](reports/screenshots/).
