Phase 9 — Data foundation: fill the lake, unify the pipeline¶
Status: Defined — ratified 2026-07-30; entry conditions cleared 2026-08-02; doc refreshed 2026-08-03 (post-ratification drift reconciled: the #436 ingest-scale chain and the Phase-8 exit fix wave landed most of Stream B3; B5 rewritten per #481's closure; audit re-verified against code). All six decisions resolved by owner; recommendations approved as written, including the reassessment refinements. Produced from a planning session: full codebase dataset-consumption audit + web-verified open-data source scan (July 2026). Ready for
/phase-splitnow. Predecessor: Phase 8 (milestone 21/21 closed 2026-08-02; open tail absorbed here — of it, the #436/#441/#443–#446 ingest-scale chain is now fully closed, #405 landed 2026-07-30; what remains open is #412 WF/WS sourcing and the four mid-review walkthrough defects, see entry/exit). License posture: unchanged — per-dataset license is RECORDED on every ingested artifact but license resolution stays deferred (standing rule: nothing publishes publicly before the licensing decision).
Goal — the Full-Catalog walkthrough¶
At phase end, from a clean stack pointed at the Hetzner object store, in one sitting, manually, through the real UI and API:
- Open the catalog → browse ≥6 hazard types (river flood, coastal flood, wildfire, windstorm, heatwave, earthquake; drought and TC stretch), multiple exposure datasets, and seeded impact functions — every entry carrying source / license / citation / version / resolution / scenario metadata.
- For at least two hazards, pick between ≥2 alternative datasets for the same peril (e.g. river flood: JRC vs Aqueduct; heatwave: EURO-CORDEX vs CMIP6-derived) and see the choice reflected in results + provenance.
- Run an impact computation on every computable peril; see indicator/score layers for every indicator peril (distinction defined below).
- Run the scenario matrix on scenario-conditioned data for ≥2 hazards.
- Every dataset on the map has working tiles; every lookup answer names the dataset it came from.
- All of it ingested through one pipeline (no side-door scripts writing directly to the DB), from documented, re-runnable source manifests.
Computable vs indicator perils (honest framing, from the vulnerability-curve scan): RF/WS/TC/EQ have machine-readable damage curves → full EAI/EP compute. Heatwave and drought have no published building-damage curves — they enter as hazard-indicator perils: ingested, tiled, scored, shown on the score card, excluded from EAI until a defensible curve exists. Wildfire is conditional (#481, closed 2026-08-01): damage curves exist (the seeded Lüthi brightness-temperature sigmoids) but every picked open source is FWI — a weather-only fire-danger index no damage function can consume in principle. WF therefore ships as an indicator peril on the FWI packs, and becomes computable only if intensity-grade footprints (brightness temperature / FRP) are sourced under #412 (stretch, Stream B5b). The UI says so explicitly.
Entry conditions¶
- [x] Phase 8 exits — cleared 2026-08-02: milestone 21/21, Mid-Review ran, exited under option A (amend and exit) — the walkthrough ran against prod (1/6 moments passed) and the criterion was deliberately waived, with the four blocking defects carried here (#548/#549 closed 2026-08-03; #558/#550 fixes deployed on v0.6.0 + ui v0.3.0, closing on the owner walkthrough — see exit criteria).
- [x] CDS + EWDS access provisioned — cleared 2026-08-02, tokens +
endpoints stored as deployment secrets. Verified 2026-07-30:
one ECMWF account signs into both stores (no separate registration);
what is per-store is the API endpoint (
https://cds.climate.copernicus.eu/apivshttps://ewds.climate.copernicus.eu/api), the Personal Access Token copied from each store's profile page, and the per-dataset license acceptance (manual, at the bottom of each dataset's download form). Tokens + endpoints stored as deployment secrets. Week 0; nothing in streams C/E for wildfire/windstorm/heatwave/drought moves without this.
Codebase audit — where we actually stand (2026-07-30, re-verified 2026-08-03)¶
Full audit in the planning session; load-bearing facts. Re-verified against code 2026-08-03 after the #436 chain + Phase-8 exit fix wave landed — two items flipped to fixed (marked), the rest still hold:
- 4 hazard types, closed enum; only river flood has real data (JRC full-Greece historical + Aqueduct scenario pack via scripts). WF ingest is blocked by a category mismatch, not a missing curve (#481): the ingestor declares FWI, a danger index no damage function can consume in principle — the unit gate is correctly rejecting it, the seeded Lüthi Kelvin curves are fine. WS has neither data nor a v98 raster. TC has layer specs but no ingest path for tracks.
- No
exposure_datasetstable — exposure "datasets" are a shared UUID + per-row duplicated sha256;ExposureType.BUILDINGSandvalue_unit="USD"are hardcoded on upload (the GHSL pack stores m³ labelled USD). - ADR-029 is largely unimplemented: zero manifest writers, no
catalog.v1.jsonproducer (/v1/admin/catalog404s on every deployment), and the real keys (hazards/,results/,staging/,chunks/,raw/{hostname}/…) are off-spec. - ~~#442 grid-membership assignment is dead code in prod~~ — FIXED
2026-08-03: the ingest chord now stamps
grid_transform/grid_width/grid_height+ per-centroidpixel_index(aggregate_and_commit.py), and all 8 pre-v2 prod datasets were backfilled viascripts/backfill_grid_geometry.py(24/24 bit-exact). - ~~COGs only happen when
raw_raster_keyis passed~~ — FIXED 2026-08-03 (PR #568): every chord now ends in_schedule_cog_build()building the COG from the committed NPZ; producers can no longer passraw_raster_key(the old path survives only as the legacyconvert_to_cogroute). Prod COGs backfilled 8/8. - Two datasets per peril can coexist but not be chosen (still true):
/v1/risk/*picks "newest with a COG" (nodataset_idparam), the matrix resolver — now scenario/supported_years-conditioned — is still newest-wins within a group with no provider dimension, and risk-surface keys (SurfaceKey) omitdataset_id(two same-peril datasets produce colliding surfaces). Result cache and centroid assignments are the only multi-dataset-safe pieces. - IngestAdapter ABC (Phase 7.1 / ADR-033) does not exist; the three
DatasetSourcepolling adapters point at invented manifest URLs and have never run against a live provider. - Dataset metadata gaps: no resolution, bbox, temporal coverage, upstream version, GCM identity, scenario taxonomy (a frozenset in the API layer), region vocabulary, or dataset status/supersession.
- Phase-8 walkthrough defects (post-ratification, carried in per plan.md): #548 (COG never built) and #549 (exposure layer wiring) closed; #558 (matrix centroid preflight) and #550 (uploaded portfolio rows in the rollup, ADR-047) have their fixes deployed on prod (v0.6.0 + ui v0.3.0, 2026-08-03) and close when the owner walkthrough passes — see exit criteria.
Source decisions per dataset type¶
License column is recorded risk, not a blocker (deferral rule). ✔ = primary pick; ◇ = user-selectable alternative; ✗ = rejected/deferred with reason. Greece-first coverage, Europe where the same download gives it for free.
Hazards¶
| Peril | Pick | Dataset (access) | Scenarios | License note |
|---|---|---|---|---|
| River flood | ✔ | JRC river flood Europe/Med 100 m (direct, already ingested for Greece) | baseline, 9 RPs | CC-BY |
| ◇ | WRI Aqueduct Floods v2 ~1 km (GEE/download; already scripted) | 2030/2050/2080 × RCP4.5/8.5 | open, attribution | |
| ◇ | Paprotny/4TU pan-EU ~100 m (direct ZIP, ~5 GB) | RCP-driven to 2100 | CC0 | |
| Coastal flood | ✔ | JRC LISCOAST extreme sea levels (direct) + Aqueduct coastal depth grids | RCP4.5/8.5 | ≈CC-BY |
| Wildfire | ✔ | GEFF/ERA5 FWI historical 0.25° (EWDS, authed) | 1940→present | CC-BY |
| ✔ | C3S FWI projections 0.11° v2.0 (CDS, authed) | RCP2.6/4.5/8.5 → 2098 | CC-BY | |
| ◇ | EFFIS burnt areas (WMS/request) — event overlay, not RP hazard | — | CC-BY | |
| note | All picked WF sources are FWI → indicator-only (#481: no damage curve can consume FWI in principle). Computable WF needs intensity-grade footprints (brightness temp / FRP) sourced under #412 — stretch, Stream B5b | |||
| Windstorm | ✔ | C3S windstorm tracks + footprints 1940→present 0.25°/0.016° (CDS) | historical | CC-BY |
| ✔ | WISC synthetic set, 22,980 footprints 4.4 km (CDS) — probabilistic complement | present climate | CC-BY | |
| note | No scenario-conditioned WS footprints exist anywhere — deriving from CORDEX winds is future in-house work, recorded as a gap (closes #412's scenario half honestly) | |||
| Heatwave | ✔ | C3S heat waves & cold spells EURO-CORDEX 0.1° (CDS) | RCP4.5/8.5, 1986–2085 | CC-BY |
| ◇ | CMIP6 extreme indices + heat stress (CDS) | 4 SSPs | non-standard licence — flag | |
| ◇ | ERA5/ERA5-HEAT (CDS) — historical truth | 1940→ | CC-BY | |
| ✗ | E-OBS — non-commercial licence; research-mode only | |||
| Earthquake | ✔ | ESHM20 hazard maps/curves (EFEHR GitLab + web services) | static (RPs) | CC-BY |
| ✗ | GEM global maps — NC/SA clauses; fallback only, never in the insurer product path | |||
| Drought (stretch) | ✔ | ERA5-Drought SPI/SPEI monthly (CDS) | historical | CC-BY |
| ◇ | SPEIbase v2.11 (direct); ISIMIP3b (only scenario-capable option) | SSPs | CC-BY / CC0 | |
| TC (stretch, non-Greek†) | ✔ | CLIMADA Data API 150-arcsec footprints (REST, HDF5) | RCP-scaled 2040/60/80 | CC-BY |
| ◇ | STORM v4 + STORM-CC (4TU, CC0) — needs windfield derivation | RCP8.5 | CC0 |
† Verified 2026-08-12, not just a scope choice: the CLIMADA Data API does return
country_iso3alpha=GRC-tagged tropical_cyclone records (both the 150as IBTrACS
random-walk family and the 300as STORM family), but every sampled HDF5 file has an
empty intensity/data array for Greece — real events/centroids, zero non-zero wind
intensity. "Non-Greek" here is a verified data constraint (no track's footprint
reaches Greece in either product), not merely a peril-breadth preference. See
#624.
Exposure¶
| Pick | Dataset | Role |
|---|---|---|
| ✔ | LitPop (ETH) | default asset-value exposure (what EIOPA's CLIMADA-App also defaults to) |
| ✔ | GHSL R2023A BUILT-S/BUILT-V + GHS-POP 100 m (direct, no auth) | building-stock + population proxies; fix the m³→currency story with a documented €/m³ factor |
| ✔ | Eurostat Census 2021 grid 1 km / 100 m pop (GISCO) | Greek socio-economic truth layer |
| ◇ | WorldPop 100 m | population alternative |
| ◇ | Microsoft ML Building Footprints + OSM/Geofabrik Greece | building footprints (EUBUCCO weak in Greece; Google Open Buildings absent in Europe) — ODbL, share-alike recorded |
| ✗ | GEM Global Exposure open release | Admin-1 aggregation only + NC-SA |
Impact / vulnerability functions¶
| Pick | Source | Note |
|---|---|---|
| ✔ | JRC/Huizinga 2017 depth-damage xlsx (direct) | re-derive parameters from the published xlsx/papers, not from CLIMADA's GPL-3.0 code — keeps the engine-isolation and license boundary clean |
| ✔ | ESRM20 vulnerability/fragility CSV+XML (EFEHR GitLab, CC-BY) | earthquake curves incl. Greek taxonomies |
| ✘ | ~~FWI-calibrated wildfire curve (literature)~~ — no such curve exists | See #481. FWI is a fire-danger index (weather-only, dimensionless), not a hazard intensity a structure experiences, so a damage function cannot be calibrated on it in principle. OS-Climate physrisk surveyed the same literature and states this explicitly, landing on the same Lüthi Kelvin sigmoid this repo already seeds. The WF unit block is fixed on the ingestor side (ingest brightness temperature / FRP), not by finding a curve. |
| ◇ | Nirandjan 2024 infrastructure curve DB (Zenodo, 1,510 curves) | critical-infrastructure expansion |
| — | Windstorm: implement Klawa-Ulbrich/Schwierz parameters from papers | no curated open file exists |
Curated collections worth ingesting from directly¶
OS-Climate physrisk Zarr on AWS (s3://os-climate-physical-risk, no-sign-request,
per-resource licenses), ISIMIP (CC0-leaning), World Bank CCKP S3, CDS ECDE
indicators. These are sources, not architecture — we re-host what we ingest.
Storage & metadata design (the "how we store" decisions)¶
- Inventory-as-contract (pattern copied from physrisk): the catalog is a
machine-readable inventory where every dataset entry carries
hazard_type/category, provider, version, path template with {scenario}/{year} placeholders, scenarios→years map, resolution, bbox, CRS, units, license, attribution, viz metadata. Ours lives in Postgres (not a JSON file) with the ADR-029 per-leafmanifest.jsonas the object-store mirror — implemented at last, or the ADR is amended honestly. - Dataset identity columns on
hazard_datasets(+ newexposure_datasetstable): provider/source id, upstream version, native resolution, bbox, temporal coverage, GCM/ensemble,data_type(fluvial/pluvial/coastal…), status (active/superseded). Scenario taxonomy becomes a table with the EIOPA↔RCP/SSP mapping from Phase 8.9's doc; region gets a vocabulary linked toadmin_boundaries. - Raw zone on Hetzner: original files land at
raw/{source}/{data-type}/{version}/{dataset-id}/with manifest, checksum, license — re-ingestable without re-downloading (Copernicus queues are slow; the raw zone is the cache). Processed stays npz+COG; Zarr is deliberately deferred (revisit if/when multi-scenario chunked point reads become the bottleneck — record as parked, not rejected). - Key-layout reconciliation — legalize or migrate
hazards/,results/,staging/,chunks/; fixraw/{hostname}/…to ADR-029 form. Not covered by ADR-045, which is scoped to ingest-scale compute/storage decisions and says nothing about MinIO key layout; this remains open for a Stream A2 ADR. - ThinkHazard-style precedence rule recorded per peril for overlapping datasets: quality score → local-over-global → recency (replaces silent newest-wins).
Streams¶
Stream A — Contracts (schema + storage identity) — first, everything depends on it¶
- A1: dataset identity migration(s) +
exposure_datasetstable + scenario/region taxonomies; ADR. (tier 4) - A2: manifest writers +
catalog.v1.jsonproducer + key reconciliation; needs its own ADR — ADR-045 does not cover key layout. (tier 3–4) - A3: multi-dataset safety:
dataset_idinto risk-surface keys, explicit dataset selector on/v1/risk/*and the matrix resolver (provider dimension + precedence rule), unit-aware impact-fn resolution. Design point: when a user picks a dataset, the matrix resolver must pin the provider family across cells (a JRC baseline must resolve to JRC scenario siblings, not silently mix in Aqueduct cells) — falling back across providers only explicitly, per the precedence rule. (tier 4)
Stream B — Pipeline unification (ETL)¶
- B1: IngestAdapter ABC + registry (Phase 7.1 pulled forward, private, no PyPI); refactor GeoTIFF modes 1/2 + exposure CSV/XLSX behind it. ADR-033. (tier 4)
- B2: new adapters: NetCDF (unlocks every CDS/EWDS dataset above), raster→exposure (GHSL/WorldPop path out of scripts), point/track (TC) as stretch. (tier 4)
- B3: ADR-045 remnant — most of the original chord-completion scope landed
before the split (grid definition + pixel_index stamped at ingest and COG
built inside the chord, both fixed 2026-08-03; absorbed #436/#443/#444/#445
all closed 2026-08-01→03). Topic 1 (out-of-core aggregation — streaming
disk-backed union/CSR-assembly, gating pan-EU #447) landed 2026-08-05 via
#595. Topic 2 landed 2026-08-06 via #596 (ADR-057): both write sites
retired, the
core/centroid_assignment.pymembership-join redesign done (dense → grid arithmetic, sparse → the artifact's pixel-index array, legacy → the unchanged join), andhazard_datasets.footprintadded as the dataset-level extent. What remains is ops, not code: backfilling legacy datasets onto v2 and then deleting the existing ~4.29 GB of rows. (tier 3–4) - B4: real
DatasetSourceadapters — CDS/EWDS API client (authed), JRC open-data, Zenodo/4TU, AWS Open Data S3 — replacing the three fictional manifest URLs; polling → review → approve exercised against a live provider end-to-end once. (tier 3) - B5: wildfire posture per #481's closure (2026-08-01: no FWI-calibrated
damage curve exists in principle — FWI is a weather-only danger index;
do not re-open that search, two investigations already dead-ended there).
Two halves: B5a — FWI packs enter as score/indicator layers via
score_schemes, riding C1's indicator-peril mechanism (no EAI on FWI, UI-labelled); B5b (stretch) — source intensity-grade footprints (brightness temperature / FRP) under #412 and pair them with the already-seeded Lüthi curves — no new coefficients needed; this is what makes WF computable. Absorbs the #412 wildfire half. (tier 3)
Stream C — Hazard expansion¶
- C1: new
HazardTypemembers + per-type ingestor/curve/score-scheme/LayerSpec wiring, plus the indicator-peril mechanism (peril without curve → no EAI, score-card only, UI labelled). One issue per peril: coastal flood, heatwave, earthquake (ESHM20 rasters through the existing engine — RP grids × ESRM20 curves; OpenQuake stays wave 2 for the modelled seismic story), drought (stretch), TC data (stretch). Migration caveat for the checklist: PostgresALTER TYPE ... ADD VALUEonhazard_type_enumcannot be used in the same transaction that adds it — the enum-add and any seed rows referencing the new value must be separate Alembic revisions (or use autocommit blocks). (tier 3–4 each) - C2: windstorm data-in (C3S footprints + WISC) + Klawa-Ulbrich parameters; closes #412's windstorm half. (tier 3)
- C3: OpenQuake scoping spike (docs-only, timeboxed — decision 3's anti-deferral anchor): (a) integration scoping — model selection, semantic mapping to our hazard/exposure contracts, sidecar posture, effort estimate — authored as a Phase 10 entry condition for wave 2/#366; (b) verdict on whether screening-grade ESHM20-based EQ EAI is defensible or earthquake stays indicator-only until OQ lands. (tier 3)
Stream D — Product surface (multi-source choice)¶
- D1: UI dataset picker per hazard (listing endpoint already filterable) + dataset provenance chip on results/lookups. UI-repo issues. (tier 3)
- D2: catalog browser surface upgrade (metadata-rich admin + user-facing catalog). (tier 3)
- D3: SDK parity — the Python SDK (Phase 5 artifact) gains the dataset selector params and catalog metadata fields, so the dual-track (portal + API/SDK, identical semantics) story holds. (tier 2)
Stream E — The actual filling (data packs on Hetzner)¶
- E1..En: one issue per pack (peril × source), acceptance = downloaded to raw zone with manifest → ingested via the unified pipeline → catalogued → tiled → computable/scored → visible in UI. Greece-first; EU-wide where the same artifact covers it. Sequenced after the A/B items each pack needs, but sourcing/downloading starts day 1 (slowest lead times — CDS queues, account provisioning). The first pack issue also creates the volume-budget ledger (expected raw + processed GB per pack, recorded in the pack manifests before download) — the artifact the exit criterion checks. The initial pack list, from the ✔/◇ picks above (split may re-batch, not silently drop — the two ◇ packs are load-bearing for the ≥2-hazards×≥2-datasets exit criterion):
- E1 river flood — JRC (re-ingest the existing Greek data through the unified pipeline; retires the side-door scripts)
- E2 river flood ◇ — Aqueduct Floods v2 scenario pack
- E3 coastal flood — LISCOAST extreme sea levels + Aqueduct coastal grids
- E4 wildfire — GEFF/ERA5 FWI historical (EWDS; indicator, B5a)
- E5 wildfire — C3S FWI projections (CDS; indicator, B5a)
- E6 windstorm — C3S tracks + footprints + WISC synthetic set (CDS)
- E7 heatwave — EURO-CORDEX heat waves (CDS; indicator)
- E8 heatwave ◇ — ERA5-HEAT historical and/or CMIP6 indices (license flag)
- E9 earthquake — ESHM20 maps/curves + ESRM20 vulnerability (EFEHR)
- E10 exposure — LitPop default asset values
- E11 exposure — GHSL BUILT-S/BUILT-V + GHS-POP (with the documented €/m³ factor)
- E12 exposure — Eurostat Census 2021 grid (GISCO)
- E13 vulnerability — JRC/Huizinga depth-damage re-derivation (from the published xlsx, not CLIMADA code) + Klawa-Ulbrich WS parameters
- E14 (stretch) drought — ERA5-Drought SPI/SPEI (CDS; indicator)
- E15 (stretch) TC — CLIMADA Data API footprints (non-Greek† — verified data constraint, see above and #624)
- E16 (stretch) wildfire intensity — BT/FRP footprints (B5b, #412)
- E-final: demo/seed migration — once the real Greek river-flood pack is in,
seed_demore-points at real catalog data (or its synthetic tif is kept only as the explicit offline fallback, labelled as such); the demo stops being the only data story. (tier 2)
Decisions (all six ratified 2026-07-30 — recommendations approved as written)¶
- Geographic envelope — DECIDED 2026-07-30: Greece-first packs, EU-wide only for coarse (≥1 km) datasets. Pan-EU full-resolution expansion is tracked in #447, gated on implementing ADR-045's out-of-core aggregation decision (topic 1; #446 itself is now resolved as ADR-045 — direction decided, implementation deferred to Stream B3).
- Copernicus CDS + EWDS access (operational prerequisite): wildfire, windstorm, heatwave, drought sources are all behind authed (free) access. Corrected after live verification (2026-07-30): one ECMWF account covers both stores — CDS and EWDS are sibling portals behind ECMWF sign-on; the per-store parts are the API endpoint, the Personal Access Token from each store's profile page, and per-dataset license acceptance. Tokens are issued to an individual — usable as deployment secrets, bus-factor-noted in the ops doc. Retrieval is queue-based: packs batch their requests, and the raw zone is the once-only cache. Recommendation: wire up both, week 0.
- Earthquake v1 posture: ESHM20 RP rasters through the existing engine now (CC-BY, closes the Greek peril-mix gap) vs waiting for OpenQuake wave 2. Caveat surfaced by reassessment: mapping PGA rasters onto ESRM20 fragility (natively Sa(T)/taxonomy-keyed; OpenQuake does that convolution properly) is a real simplification — v1 EAI is screening-grade and must be labelled so; the EMS-98/macroseismic form of the ESRM20 curves is the cleaner v1 match to raster intensities. Recommendation: ship v1 now as score card + screening-grade EAI behind the honest label, and add an OpenQuake scoping spike (docs-only, timeboxed) to Phase 9 with two deliverables: (a) integration scoping (model selection, semantic mapping, sidecar posture, effort) as a Phase 10 entry condition — the anti-indefinite-deferral anchor for wave 2/#366; (b) a verdict on whether screening-grade EQ EAI is defensible or earthquake stays indicator-only until OQ.
- Zarr adoption: defer (parked) vs adopt for scenario stacks now. Recommendation: defer; keep npz+COG; copy only physrisk's metadata pattern. Two sharpening notes: (a) our point-serving plane is already the Phase 8 answer layer (H3 RiskCells + COG point reads) — adopting Zarr would add a third plane that overlaps it, which strengthens the deferral; (b) deferring Zarr as our storage format does not preclude reading Zarr sources at ingest time (physrisk's AWS holdings) via an xarray/zarr dep in the ETL — that is just another source format behind the IngestAdapter.
- Absorption — DECIDED 2026-07-30: Phase 9 absorbs #412, the #436 chain (#443/#444/#445/#446), and Phase 7.1; Phase 7 re-cut shrinks to public-facing items only. #442 and #405 landed same day (PRs #449, #448) before absorption mattered; #446 landed 2026-07-30 as ADR-045 (direction decided, implementation deferred to Stream B3). Status 2026-08-03: the entire #436/#441/#443/#444/#445 chain is closed pre-split — only #412 and the ADR-045 implementation (B3 remnant) carry forward as open work.
- Heatwave/drought honesty rule: OK to ship as indicator perils (no EAI)? Recommendation: yes — matches what ThinkHazard/score-card products do. Mechanism refinement from reassessment: "indicator peril" should be derived from curve availability per (peril × exposure type), not a hardcoded peril list — a peril with no seeded default curve simply gets no EAI path, and the UI renders the score/indicator card. This keeps the door open for the curves that do exist in these families (drought crop-yield for agri exposure; heat-mortality for population exposure — a different, touchier monetization story than property EAI, deliberately not v1).
Exit criteria¶
- Wave 0, before the data waves: ~~the Phase 8 six-moment Showcase walkthrough passes against production, closing #558 and #550.~~ CLEARED 2026-08-10 — the owner reports the walkthrough passed against prod; #558 was already closed 2026-08-04 and #550 is closed on that report. The Showcase claim is no longer gated. This does not close Phase 9: the exit is the Full-Catalog walkthrough plus the ops track, both below.
- The Full-Catalog walkthrough (Goal) runs manually end-to-end — API and UI.
- Every dataset in the catalog has: source manifest, license + attribution
recorded, working tiles, dataset-attributed lookups/results.
Amended 2026-08-10 by ADR-070 — this criterion
originally read "raw-zone copy + manifest". The raw zone is a transient
staging concept, not an archive: every pack streams its download to local
disk, clips/ingests, then deletes it, so no raw-zone copy exists for any of
the ten packs and none ever will under the current design (verified
2026-08-08 —
raw/holds only.keep+catalog.v1.json, ~0.03 MB). The reproducibility artifact is the per-dataset source manifest athazards/{dataset_id}/source_manifest.json(ADR-066); pre-ADR-066 datasets have theirs atstaging/{ingest_job_id}/manifest.json. The Full-Catalog walkthrough audits that, not araw/copy. - ≥2 hazards offer ≥2 user-selectable datasets with the precedence rule documented.
- Zero side-door ingests remain:
scripts/ingest_*.pyeither retired or reduced to thin callers of the unified pipeline. - One live polling→review→approve→ingest cycle demonstrated against a real provider.
- OpenQuake scoping doc merged (C3) — the Phase 10 entry condition for wave 2/#366 exists in writing: docs/plan/exploration/06-openquake-scoping.md.
- Storage bill on Hetzner checked against a recorded volume budget (raw + processed per pack, tracked in the pack manifests).
Risks¶
- Copernicus lead times (queues, account provisioning) — start Stream E sourcing day 1, in parallel with contracts.
- Volume creep on the box — the raw zone is a cache with a budget line per pack, not a mirror of the internet; packs record expected GB before download.
- License findings pile up (ODbL share-alike family, CMIP6 non-standard licence, ELSTAT unverified) — recorded per dataset now; a single license-review session after the catalog exists is the plan, per the deferral rule.
- Enum-driven hazard coupling makes each new peril a cross-cutting change — C1's first issue should carve the per-peril wiring into one documented checklist (ingestor, curve, score scheme, LayerSpec, chunk budget, engine code) so subsequent perils are mechanical.
- Windstorm scenario gap is unfixable with open data — the phase ships WS historical + synthetic and says so; deriving CORDEX-based footprints is its own future project.