Skip to content

Phase 8 — Product foundations + ORSA module (wave 1)

Status: Defined (exploration session 5, 2026-07-26); reframed showcase-first same day (owner direction) — build fast toward a defined Showcase; commercialization mechanics deferred until an external user exists. Ready for /phase-split. Scope: the A2-sequenced track — foundations properly built, then the ORSA-shaped report module on top; no external gates between streams. Source decisions: exploration/02-personas.md (personas + product sequencing), exploration/04-answer-layer-rfc.md (ratified RFC, amendments A1/A2/A3), exploration/05-rat-convergence.md. Predecessor: Phase 6b (closed). Phase 7 remains parked pending re-cut — its adapter items return after the licensing verdict; nothing in Phase 8 publishes anything.

Goal — the Showcase

Reframed 2026-07-26 (owner direction): this is a showcase-stage project, not a product with customers. The phase is showcase-driven: build fast, make capability visible, defer commercialization mechanics (licensing, GDPR formalities, sign-offs, channel validation) until someone external actually shows up. The market-validation questions from 02-personas remain questions to ask opportunistically — nothing in this phase blocks on them. One standing rule survives because it is free: nothing publishes publicly (code, images, packages) until the licensing decision — repos are private anyway.

The Showcase walkthrough — the phase's verifiable goal. From a clean stack, one sitting, no hand-waving:

  1. Search a Greek address (or drop a pin) → instant multi-hazard score card with plain-language bands and a "why this score" drawer.
  2. Open the map → hazard/exposure/result layers render (tiles working in prod).
  3. Upload a portfolio CSV → per-asset scores + book rollup appear without a compute wait.
  4. Run a scenario matrix (2 climate scenarios × 3 horizons) on real scenario-conditioned Greek data → watch the batch complete.
  5. Run the same scenario through two engines (climate-lama-engine + CLIMADA sidecar) → see the agreement view.
  6. Download a composed, provenance-cited PDF report pack (the ORSA-shaped template) from the UI.

Every work item below exists because it makes one of those six moments real.

Amendment 2026-08-02 (owner direction) — the Showcase walks river_flood only, and says so. §Risks below pre-authorised this: "if a peril lacks usable projections, the showcase ships with fewer perils and says so." That risk materialised. Both Greek catalogs record river_flood as ingested and wildfire / storm_europe as status: gap — source-verified 2026-07-27, with the rejected sources and reasons inline (scripts/scenario_hazards_manifest.json, scripts/historical_catalog_manifest.json); tropical_cyclone is out of region, not missing. So moment 1 demonstrates one peril, not four, and moments 2–6 run on flood. Sourcing the remaining perils stays in Phase 9 via #412 — pulling it back here would invert the dependency and stall the exit gate. Evidence and the alternatives rejected: phase-8-walkthrough-readiness.md; findings: mid-review-phase-8.md F-1.

Entry conditions

  • [ ] GitHub Actions unblocked (account billing self-resolves 2026-08-01) → dispatch ci.yml + sdk-smoke.yml green on main in all three repos — the never-yet-exercised validation of the on-demand CI model.
  • [ ] Beat + titiler compose changes (#368/#369) rolled out on the prod host (manual docker compose pull && up -d per DEPLOYMENT.md; automation is #356, not a gate).
  • [x] Answer-layer RFC ratified (A1/A2/A3). Epics #366/#367 aligned.

Out of scope (deliberately)

  • Anything publishing code or images publicly (the one free-standing rule kept from the commercialization track).
  • Commercialization mechanics: licensing resolution, GDPR/processor formalities, actuarial sign-off, channel/pricing validation — deferred until an external user/pilot exists. Recorded, not scheduled.
  • OpenQuake/seismic (the quarter-plus project — wave 2, #366). The CLIMADA sidecar (8.15) covers the multi-engine showcase moment instead.
  • Portfolio workbench full product surface (#367 — wave 3); this phase ships the upload→scores→rollup slice only (showcase moment 3).
  • Public citizen funnel deployment (the lookup + score card ship here and demo fine; putting them on the open internet waits for the deferred items above).
  • Transition-risk ORSA content (the consultant's half of a real filing).
  • Desktop counterpart — parked; the web UI is the showcase surface. When wanted, a Tauri/Electron wrap of the existing UI is the cheap path (weeks, not a rebuild) — record as a parking-lot candidate, not phase scope.

Stream F — Foundations (start immediately, parallelizable)

8.1 — Fix #370: eai_exp position→centroid mapping (bug, load-bearing)

  • [ ] Failing test first: sparse/non-contiguous centroid assignment → geojson and report top-exposures attach EAI to correct coordinates (acceptance in #370).
  • [ ] Fix by re-joining exposure rows with the compute-time ORDER BY; correct the models/result.py docstring ("per exposure point", not "per-centroid").
  • Why in Stream F: the surface writer (8.4) inherits this exact trap otherwise.
  • Suggested /analyze tier: 3.

8.2 — Report composer (the shared primitive)

  • [ ] Replace the placeholder PDF path in reports.py with a real composer: template-driven sections (cover, summary metrics, charts, methodology with provenance-stamp citations, attribution annex).
  • [ ] Renderer decision recorded as ADR-039* ("Report generator choice"): WeasyPrint-in-worker vs Quarto container — decide during build; WeasyPrint is the default lean candidate, Quarto the heavy fallback (inverts old 7.4).
  • [ ] Celery task render_report(scenario_id, template=...); 202 + job polling; PDF to object store per ADR-029 layout.
  • [ ] One generic template shipped and smoke-rendered in tests.
  • Suggested /analyze tier: 4 (worker + template + storage + API).

8.3 — Answer-plane schema: RiskSurface + RiskCell

  • [ ] Migrations per the ratified RFC with A3.4: reference tables org-less (like admin_boundaries, no RLS) and org-scoped tables under the standard FORCE-RLS pattern — two table pairs, one contract. Isolation invariant in the migration docstring: org-derived cells never readable cross-org; promotion to reference is manual.
  • [ ] H3 index columns + generated geometry for tiling; DB cells capped at r8.
  • [ ] Size accounting: per-surface rows/bytes recorded (usage_events pattern); surfaced in /metrics.
  • [ ] Retention: org run-saves evictable (owner can re-run); reference surfaces replaced atomically by versioned rebuilds only (A3.6). Miss semantics defined in the API contract: partial answer + "surface not built here" provenance.
  • [ ] ADR-040* "Two-plane answer layer" authored from the RFC.
  • Suggested /analyze tier: 4.

8.4 — Surface writer (factory-side run-saves)

  • [ ] Worker writes spatial results into org-scoped cells on every impact run (A1 default-on; single-digit MB/run bound verified in the reassessment), geolocating by exposure re-join (8.1's rule), never array position.
  • [ ] Deliberate reference-surface build path: a management task that runs a designated scenario matrix and writes org-less reference cells.
  • [ ] Depends on: 8.1, 8.3.
  • Suggested /analyze tier: 4.

8.5 — Asset entity + GDPR posture

  • [ ] Asset model per ADR-041* (identity: address, geocoded point, attributes JSONB, org-scoped, score history) + portfolio_assets junction (A3.5) with a migration story for existing exposure-based portfolios.
  • [ ] One-directional Asset→Exposure generation service; dataset_sha256 recipe for generated batches = hash over canonicalized rows (A3.5) so result-cache and provenance keep working.
  • [ ] A short data-handling note in the Asset ADR (addresses are personal data; design erasure-friendly: Asset deletable without breaking result provenance). The formal GDPR/processor posture is deferred until an external user exists — this note just keeps the schema from painting us into a corner.
  • Suggested /analyze tier: 4 (schema + generation service).

8.6 — Score schemes v1 (feature-flagged)

  • [ ] score_schemes versioned + seeded with citations (published thresholds — JBA/ThinkHazard-style ordinal categories as v1 per the ratified ship-and-polish decision); banding precedence per A3.3: flood/wildfire band from COG point intensity, cell bands for rollups/smooth hazards.
  • [ ] Rollup functions: asset, portfolio (value-weighted + worst-N), admin unit (cells within GADM polygon).
  • [ ] Behind a feature flag; advisory note recorded: external actuarial/scientific review before the first paid engagement.
  • [ ] ADR-043* "Score schemes + rollup semantics".
  • Suggested /analyze tier: 3.

8.7 — Geocoding adapter + Nominatim (Greece)

  • [ ] core/geocoding.py interface + Nominatim adapter (the only file importing the provider client); Greece OSM extract, self-hosted service in compose (dev + prod), sized in 8.11's capacity note.
  • [ ] Low-confidence fallback contract: pin-drop path needs no geocoder.
  • [ ] ADR-044* "Geocoding adapter + provider".
  • Suggested /analyze tier: 3.

8.8 — Point lookup read path

  • [ ] GET /v1/risk/lookup?lat&lon[&scenario&horizon] — new code (A3.3: no point path exists): COG point read (titiler /cog/point proxy or rasterio window)
    • org/reference cell fetch + score card assembly with provenance refs.
  • [ ] Latency budget stated and tested (cells indexed lookup; COG read measured, not assumed).
  • [ ] Depends on: 8.3, 8.6; titiler prod rollout (entry condition).
  • Suggested /analyze tier: 4.

8.9 — Data foundation: Greek multi-hazard, multi-scenario catalog

The weakest layer of the stack today is data: only historical JRC river flood (bbox demo) + LitPop exposures. A showcase lives or dies on data richness — this is a headline stream, not a checkbox.

  • [ ] Scenario-conditioned hazard datasets for Greek climate perils ingested and cataloged: river flood mandatory; wildfire/windstorm as source availability allows — each at ≥2 climate scenarios × ≥3 horizons, from original public sources (05-convergence rule: own pipeline, own attribution).
  • [ ] Broaden the historical catalog while at it: full-Greece (not bbox) RF at usable resolution; WF/WS national coverage; a curated Greek exposure set beyond LitPop (building-stock proxy) — the same dataset that seeds the national reference surfaces (8.4).
  • [ ] A citable mapping doc: EIOPA below-2°C / well-above-2°C → concrete RCP/SSP dataset choices per hazard × horizon (docs/concepts/).
  • [ ] Demo scenario matrix runs end-to-end on the ingested pack.
  • Note: today "time horizon" is growth extrapolation — this item is what makes the scenario story honest.
  • Suggested /analyze tier: 4 (data sourcing + science documentation). Start FIRST — least controllable lead times.

8.10 — UI: the showcase surface

  • [ ] climate-lama-ui carries the showcase moments: (1) address/pin search → score card + explainability drawer; (3) portfolio CSV upload → per-asset scores + book rollup view (thin slice, not the full workbench); (5) engine picker + agreement view on a scenario run; (6) matrix-run trigger/status/report download.
  • [ ] #355's remaining half: same-origin LayerSpec tile URLs through the proxy (backbone layer_spec.py + UI wiring) — showcase moment (2).
  • [ ] UI issues opened in the UI repo (it currently has zero open issues — the forward queue starts here).
  • Suggested /analyze tier: 4 (multiple surfaces; still slices, not products).

8.11 — Ops minimum: scheduled backup

  • [ ] Scheduled pg_dump + object-store backup with one documented restore test. (Capacity sizing for Nominatim/factory runs: solve when something actually strains the box — a sentence in the deployment doc, not a study.)
  • Suggested /analyze tier: 2.

8.15 — CLIMADA sidecar engine + agreement view (showcase moment 5)

  • [ ] Pull Phase 7.3's design forward, private-only: engine registry + X-Engine request-time dispatch in the worker; CLIMADA in an isolated sidecar container (process boundary, no in-process import — ADR-024 untouched); input/output translation to ModelInterface.
  • [ ] Agreement view: same scenario through both engines → side-by-side metrics + divergence percentages (the parity harness grows a user-visible face).
  • [ ] No PyPI, no public repo — distribution rules wait for the licensing decision; everything ships inside the private compose stack.
  • Suggested /analyze tier: 5 (cross-process IPC; the phase's hardest item).

Interview questions — opportunistic, never blocking

The 02-personas ORSA questions (willingness-to-pay, channel, incumbent tooling, cycle reality) stay on file to ask whenever conversations naturally happen. No stream, item, or merge waits on them. The persona checkpoint (P1 vs P3) remains a strategy-session topic for the exploration track, not a build gate.

Stream O — ORSA module (build when 8.2 + 8.9 land; no external gate)

8.12 — Scenario-matrix runner

  • [ ] One API call → a tracked batch (2 scenarios × 3 horizons × portfolio) of existing impact jobs; batch status, partial-failure semantics, one result set.
  • [ ] Depends on: 8.9 (data), existing compute path (unchanged — factory primacy).
  • Suggested /analyze tier: 3.

8.13 — ORSA report template

  • [ ] Composer template: per-scenario × horizon results, EP curves, horizon comparison, methodology + scenario-mapping citations (8.9's doc), reproducibility stamp, assumptions annex, explicit "physical-risk module — transition risk out of scope" framing.
  • [ ] Rendered pack reviewed against EIOPA's application-guidance mock examples.
  • Suggested /analyze tier: 4.

8.14 — Module packaging

  • [ ] Docs-site page (what it is, what it is not, how the consultant/actuary completes the filing); demo walkthrough on the seeded stack; SDK example.
  • Suggested /analyze tier: 2.

ADRs authored in this phase

ID (assigned at merge; next free is 039) Title Item
ADR-039* Report generator choice 8.2
ADR-040* Two-plane answer layer (RiskSurface/RiskCell, A1/A3 rules) 8.3
ADR-041* Asset entity + portfolio_assets + generated-batch hashing 8.5
ADR-042* Engine registry + request-time dispatch (CLIMADA sidecar, private) 8.15
ADR-043* Score schemes + rollup semantics 8.6
ADR-044* Geocoding adapter + provider 8.7

Exit criteria

The Showcase walkthrough (Goal section) runs end-to-end in one sitting — all six moments, on a clean stack. Plus:

  • Surfaces accumulate from runs with visible size accounting; reference surfaces exist for at least one hazard × scenario × horizon triple.
  • Zero public-release steps taken (the one standing rule).

Risks

  • 8.9 data sourcing is the least controllable item (availability/licensing of scenario-conditioned Greek hazard data) — start it first; if a peril lacks usable projections, the showcase ships with fewer perils and says so.
  • 8.15 CLIMADA sidecar is the hardest engineering item (cross-process IPC, CLIMADA env in a container) — it is showcase moment 5, not a dependency of anything else: if it drags, it drops to the phase tail without blocking moments 1–4 and 6.
  • Score credibility — v1 bands are self-set with citations; keep the "why this score" drawer visible in every demo so bands never stand naked.
  • Scope creep toward product polish — this phase ships showcase slices (thin portfolio view, one report template), not finished products; the wave 2/3 epics (#366/#367) hold the full versions.
  • Solo-maintainer bandwidth — sized for /phase-split + /phase-build orchestration; items are single-repo except 8.10.