Phase 18 — Property level: every answer down to the building¶
DEFINED 2026-10-28, awaiting owner lock (planning session of 2026-10-28, by owner instruction,
after mid-review-phase-17.md closed the books on Phases 17 and 17b). The
slice is chosen from the Phase 18 charter stub (about 40 deferred
items), against the development-phase goals and showcase-script.md, with a
market rescan dated since 2026-09-26. Direction: open on the Direction board of the Phase 18
prototype (link in Prototype); an unpicked item follows the recommendation, and the
owner's picks lock this plan before /phase-split phase-18. Two /reassess fast passes are
recorded below; every correction they made is folded into the text, not appended. Serves Goal A
(the data answers at the building), Goal B (a footprint layer, per-asset curves and a modelled
value through the real ingest and compute) and Goal C (Moments 1 and 2 gain the building).
What it is. The build the Phase 17 spike found feasible (exploration/property-level.md, D18): a footprint layer for Greece and Cyprus under ODbL's four rules, a footprint reference on every exposure, the damage curve bound per asset from what the building is (use, storeys, epoch), a modelled building value where the tenant has none, the building on the map at street zoom, Lookup answering for this building with an editable home, and the licence credit on every map and report. Every figure arrives through the real ingest and compute (ADR-077); every per-building figure says how coarse its hazard cell is and whether it is the tenant's own or a published ODbL figure.
What it is not. No new engine, no OED policy layers, no stochastic event sets, no new country or peril, no alerts, no billing, no street photos: those are the Phase 19+ stub. No building-scale flood hazard is sourced in this phase (D4): the ≈ 90 m cell is shown as such. No tenant-facing per-building export ships before counsel reads rule 3 (D2).
Inputs¶
| Input | What it contributes |
|---|---|
| Owner instruction, 2026-10-28 | Target users: insurers and the public sector in Greece and Cyprus; a slice sized like Phase 17 or smaller that moves a showcase moment; market rescan Phase 17 style; sourced regulation only |
| plan.md § Phase 18 charter stub | The ~40 deferred items with their source decisions (D17–D21, run model § 13, domain model § 9, fine-tune, SH-10, K57–K71) |
| exploration/property-level.md | Licence outcome (four rules), the ≤ 5 m flood minimum, the exposure-source verdict, GHSL's resolution, the ten build items in dependency order, three [ASSUMPTION]s |
| mid-review-phase-17.md | F-1 (component issues cannot reach the design), F-2 (escape rate 0.29 / 0.42), F-3 (red e2e-smoke on UI main), F-4 (ui#1249, ui#1177), F-6 (regulatory wording) |
| Backbone and UI inventories (2026-10-28) | Ground truth for every Grounding: line: backbone 97b3a970, UI ab69f14e, engine v0.6.0; the reassess pass 1 reads below |
| Market rescan (below) | What Moody's, Swiss Re, Munich Re, Climate X, Jupiter, Fathom, ICEYE and OS-Climate shipped since 2026-09-26, and the regulation with a verifiable source |
| Prototype v3 (Phase 18) | Every new or changed view, desktop and phone, with the Direction board |
The slice — three candidates, one chosen¶
| A — Property level (chosen) | B — Insured loss the market's way | C — Live risk, matured | |
|---|---|---|---|
| Story | A citizen's pin and an insurer's row both land on a building: its footprint, its storeys and use, a curve chosen for it, a value when none was given, an editable home, and the credit that makes the ODbL data usable | A book priced the way a reinsurer reads it: OED per-location and per-account terms, layers and reinsurance; per-asset curve binding; GeoTIFF exposure upload; the per-country batch; population and built-volume curves; risk_rp_100 |
The decision-maker gets told: an ADR-078 item 6 amendment, loss-threshold and e-mail notifications, assets in alerts on Home, a flood feed, forecast lead time, uncertainty bands on the event loss, drought packs |
| Stub items taken | D18 items 1–8 and 10; D21 per-asset binding; D17 polygons as footprints; K61's building tier | D19 OED layers; D21 per-asset binding and GeoTIFF upload; EX-19/K41 batch; domain-model curves and risk_rp_100 |
K28 ADR amendment; SH-10 ui#530, ui#538; K60; live-lane science; drought packs |
| Moments moved | Moment 1 (home → building, edit home), Moment 2 (per-asset curve, modelled value, footprint map, report credit), Moment 3 (engines agree on bound curves) | Moment 2 steps 5–7 (money), Moment 3 step 1 (batch) | Moment 4 (steps 3–5 and a new alert step) |
| Users in Greece and Cyprus (the owner's instruction of 2026-10-28) | Insurers under Law 5116/2024 insure buildings at ≥ 70 % of value: a per-building value and curve is what the pricing needs; municipalities count real buildings in a hazard zone; citizens see their own house | Insurers' reinsurance desks; little for the public sector or citizens | Civil protection and municipalities; little for insurers' pricing |
| Feasibility | Spike done: licence outcome, sources, order of items; the engine's exposure contract takes a per-point curve id (engine_adapter.py:27, :553), though the worker fills it with one curve today (D5); PostGIS polygon layers exist (admin_boundaries) |
OED terms are a sidecar and schema change with no screen to show; AEP needs stochastic event sets the catalogue does not hold and no open source provides for Greece | Needs an ADR amendment first, a flood feed whose licence (EFAS) is unverified, and science (ensembles, uncertainty) with no spike behind it |
| Size | ~31 issues (20 backbone, 10 UI, 1 UI harness, 0 engine) | ~22 issues, mostly backbone | ~24 issues, two spikes first |
| Risk | Three [ASSUMPTION]s to resolve (licence rule 3, the non-flood premise, the Huizinga basis); a vector loader the ingest lane does not have; the prod load of two national footprint files |
Low visible change; the event-set gap blocks AEP outright | Feed licences and the science; alerts without a false-alarm rate |
Recommendation: A. It is the largest visible move on the north star for the two target users: Moment 1 (showcase-script.md line 45) stops answering from a 4.6 km LitPop cell and Moment 2 (line 98) gains the per-asset curve and the building value that the mandatory insurance rule of Law 5116/2024 asks insurers to price. It is the deepest rung of the exploration frame's granularity ladder, the owner's own ask in the market scan (comments C98–C100 and C105, phase-17-revamp.md lines 434 and 464), the Phase 17 deferral D18 ("property level is a spike; the build is Phase 18", phase-17-revamp.md line 98) and the owner's pick on the Phase 17 Direction board (board item 13, "Spike in Phase 17, build in Phase 18"), and the one candidate whose groundwork exists (the spike's verdict, property-level.md lines 13–33 and 362–369). B's screen-visible part, per-asset binding, is inside A. C waits on an ADR amendment that is the owner's call, and its feeds are Phase 19+ work.
The frame — what each surface gains¶
Today (UI v0.21.0) |
Phase 18 | Evidence |
|---|---|---|
| Lookup — a place report over a 90 m to 4.6 km cell; "Buildings in data" counts LitPop grid points (LK-21) | The building the pin falls in: outline, storeys, use, epoch, its modelled value, a cell-size line per hazard in metres from the dataset's own resolution ("value of the ≈ 90 m cell"), a money line for the building, and Edit home (floors, epoch, value, use) that recomputes; the place report prints the building | C98–C100, C105, D18, property-level § 5 items 6, 8 |
| Explore — the exposure is a portfolio, catalogue datasets or one asset; result and exposure tiles stop at asset points from z 11 | One building as the exposure through the "My assets" door (a snapped member is the existing asset; a bare footprint is building:<footprint_id>); a building tier of the result and exposure layers from z 15 (footprints coloured by loss) where members are snapped; the asset card names the bound curve and why |
K61's building tier, D17 (polygons as footprints), D21, run model § 3.1 |
Portfolios — CSV of lat, lon, value (+ optional columns into properties) |
Upload columns occupancy, storeys, epoch in the UI's template; a Snap to buildings step with a coverage figure and an opt-in "fill missing attributes from the footprint"; member rows with their footprint, bound curve and, where the value or curve came from the footprint, the "ODbL figure" tag beside the "modelled" badge |
property-level § 5 items 3–5 |
| Data — datasets, curves, measures, events | The footprint pack card (ODbL, attribution, version, coverage per country); damage-curve rules (hazard × use × storeys × epoch → curve), seeded per org and org-editable | items 1, 2, 4 |
| Reports — portfolio and place reports | The ODbL credit printed on every report with a footprint map; the per-building export control present but off until the licence review (D2) | item 10 |
| Home — area card counts grid points | "Buildings in data" counts footprints | LK-21 |
| Adapt, Analyses, Settings, Admin | Unchanged; the footprint pack's status lives on Data's pack card, not in Admin | — |
Direction lock (owner picks on the Direction board)¶
An unpicked item follows the recommendation. Confirmed is filled from the board's shared store
before /phase-split (ArtifactData list picks on the Phase 18 artifact).
| Id | Decision | Recommended | Confirmed |
|---|---|---|---|
| V1 | Lookup building card | A — a building card under the score in the report column, the outline on the map | |
| V2 | Explore building tier | A — footprints coloured by loss from z 15, asset points beneath | |
| V3 | Portfolios snap step | A — a step of the upload flow with a coverage meter, re-runnable from the detail page | |
| D1–D14 | Direction decisions (the design record below) | as recommended |
Design record (D1–D14 — the decision, its options, the recommendation, and what reverses it)¶
- D1 — Footprint source. Options: (a) JRC DBSM R2025 (EU-27, ODbL, per-country GeoPackage; the report JRC142133 gives per-building height, use and construction epoch, property-level.md line 62); (b) Overture Maps buildings (ODbL, OSM plus ML footprints, height partial, no epoch); (c) both, DBSM first with Overture filling gaps. Recommended: (a), with (b) recorded as the fallback source and the S1 check that the R2025 folder holds Greece and Cyprus with those attributes filled. Reversed by: a DBSM file missing for either country, or attributes too thin for D5 (then (c)). S1 (2026-10-28): both files present with height, use and epoch; the rule does not fire.
- D2 — The licence rules (ODbL, four rules). Options: (a) ADR-089 is the licence review that
docs/DATA_SOURCES.htmlline 128 defers to: it decides rules 1, 2 and 4 and records rule 3 as the platform's reading, pending counsel; until counsel ratifies rule 3, no tenant-facing per-building export ships (B15's export sits behind a setting off by default, reports carry only the Produced Work credit), a member whose value (D3) or curve (D5) came from footprint attributes carriesvalue_source="modelled"orattributes_source="footprint"andlicense="ODbL-1.0", its loss is a published ODbL figure with the method page and is never mixed into a tenant-private export, and losses from tenant-supplied value and attributes stay the tenant's; (b) wait for counsel before any footprint row ships; (c) publish per-building derived figures only (no tenant join). Recommended: (a). Reversed by counsel: if rule 3 is read as written, the export switch lifts; if it is read the other way, the display-only join becomes a Produced Work that must not carry tenant values, the UI draws the outline from a separate request andfootprint_iddrops from every tenant row. - D3 — Modelled building value. Options: (a) footprint area × storeys × a country €/m² from Huizinga 2017 (JRC105688, 2010 price level) lifted by a sourced construction-cost index, labelled "modelled", published as ODbL with its method page (rule 2), used only where the tenant gives no value; (b) the ENFIA insured-value floor per m² as the figure; (c) no modelled value, tenant TIV only. Recommended: (a), with (b) printed beside it as "what the tax discount expects" once its source is read. Reversed by: the Huizinga basis being floor area rather than footprint (then the formula uses the floor area DBSM gives, or storeys × footprint as its proxy, stated). S1 (2026-10-28): the basis is footprint (§ 5.3), and the figure is a depreciated flood maximum, so area × storeys × that figure would count the floors twice. Option (a) is amended: DBSM
floor_area_m2× Table 3-25's construction cost per m² × the Eurostatsts_copilift, as set out in S1 results. - D4 — Building-scale flood. Options: (a) honest labelling: every per-building figure carries
cell_size_m, computed from the answering hazard dataset'snative_resolution(degrees per pixel,models/hazard.pylines 223–226) asnative_resolution × 111 320 × cos(lat)east–west with the north–south figure or the larger of the two beside it,nullwith "resolution unknown" when the column is null, and never the lookup's H3resolution(a different field, kept); the flood line reads "value of the ≈ 90 m cell this building sits in"; (b) source ≤ 5 m official flood maps for Greece and Cyprus (the flood-risk-management-plan maps, licence to verify per ADR-059) in this phase. Recommended: (a) now, with (b) as a Phase 19+ spike. Reversed by: a licensed ≤ 5 m source found before the split (then (b) joins as a pack issue). - D5 — Per-asset curve binding. Options: (a) rules: hazard × occupancy × storeys band × epoch band → impact function, a seeded default set per hazard keyed to the built-in curve's identity (
haz_type,exposure_type,name, never a row id, because impact functions are per org:models/impact_function.pylines 13–14 and 39) and resolved per org, seeded lazily besideseed_builtin_impact_functions, with org-level overrides; the resolver reads the tenant's ownoccupancy,storeys,epochfromexposures.propertiesfirst and the footprint's attributes only where the upload opted in (D2); the worker loads every curve the rules resolve for the run, gives each a small integer id and fills the per-pointimpf_idfrom the resolution (replacing the constantnp.onesatworker/tasks.pyline 1970), the adapter builds anImpactFuncSetof all of them at its four call sites (lines ~305, 472, 561–574, 673) and measures adapt and report every function, the provenance records the list of{int_id, impact_function_id, version, resolved_on}in place of one id (lines 274, 296, 2135) and the binding hash enters the result cache key; (b) a curve column per exposure row set at upload; (c) no binding, one curve per run (today). Recommended: (a). Reversed by: nothing in the catalogue to bind for a peril (then that peril keeps one curve and says so). - D6 — Lookup's building money line. Options: (a) a per-org system exposure dataset "homes" with one row per saved home (the footprint point, the value from the home's edits else the modelled value, the attributes), submitted through the existing
POST /v1/compute/impactwith the one-row selection, so the result cache key (dataset id and sha plus the membership hash,core/result_cache_service.pylines 123–125 and 216–241) covers footprint, value and edits with no new route; the job writes an org-scopedimpact_resultsrow that the cache row'sresult_idanchors (orphan purge,worker/tasks.pylines 5302–5307), and the edits digest is in the key; the UI shows "computing" then the figure, never a synchronous compute in the API; (b) a newPOST /v1/lookup/building-impactthat builds the one-point exposure incoreand mints its own key; (c) compute it incorefrom the curve and the cell intensity; (d) no money line. Recommended: (a):compute_service.pyline 419 refuses a request with neither dataset nor portfolio, architecture rule 2 and ADR-024 rule out (c), and (a) reuses the lane and the cache as they are. Reversed by: a measured median over 10 s for the one-row job on prod (then the cache is pre-warmed per footprint by the pack). - D7 — Edit home storage. Options: (a) tenant-private overrides keyed by footprint id (floors, epoch, value, use) in their own org-scoped table
home_editswithorg_id,FORCE ROW LEVEL SECURITYand theorg_isolationpolicy likeexposures(admin_boundariesis the precedent for the shared layer only, not for this table), materialised into the org's "homes" dataset row (D6), never written to the footprint layer; (b) a one-row exposure dataset per edit. Recommended: (a): it is the Collective Database pattern the spike relies on, and (b) would list a dataset per citizen. Reversed by: nothing foreseen. - D8 — Street photos. Options: (a) none this phase; (b) user-uploaded photos only; (c) Mapillary (CC BY-SA, a second share-alike review). Recommended: (a); Phase 19+ with (b) as its first rung.
- D9 — Non-flood perils per building. Options: (a) differentiate by vulnerability only (the bound curve), keep screening grade, label the hazard cell size, and source the "slowly varying at building scale" premise per peril before any label above screening; (b) show no per-building figure for non-flood perils. Recommended: (a). Reversed by: a source showing a peril varies at building scale (then that peril waits for its grid).
- D10 — The building tier on maps. Options: (a) the result tiles'
assetslayer is built from the result's per-asset arrays bound into the SQL (db/repositories/vector_tile_repository.pylines 280–310, from theeai_expfile in MinIO), not from a table, socore/result_asset_tiles.select_pointsfetchesfootprint_idper exposure id (an RLS-scopedexposuresread) and binds it as one more array, and the MVT SQL left-joins the footprint table on it to draw the polygon from z 15, else the point; the exposure tiles' points SQL (lines 352–371) left-joins the same way; same routes (ADR-083), a newbuildingslayer; (b) points only, with the outline drawn on select. Recommended: (a). Reversed by: the performance suite's p95 first tile over 500 ms at z 15 on the 10M fixture (a new zoom entry intile-latency.spec.ts, whose list is[4, 5, 6]today), then (b). - D11 — A building in Explore. A building is what is at risk, so it is an exposure through the "My assets" door (run model § 3.1, "exposure first"), never a clip and never a rung of the place ladder. Options: (a) a footprint with a snapped tenant member resolves to the existing
exposure=asset:<exposure_id>with no new kind; a bare footprint adds the run exposure kindbuilding(src/run/runAddress.tsparse and format,buildinginscopeKinds.tsSCOPE_KIND_IDS,set_scopeacceptingexposure=building:<footprint_id>insrc/actions/explore/setScope.ts), and the backbone's run contract (ADR-087, B18) accepts a footprint exposure as one point with the value fromhome_editselse the modelled value labelled ODbL, reusing D6's "homes" row; (b) Lookup only. Recommended: (a), through the action registry (D11 of Phase 17) and its guard test. Reversed by: the owner judging Explore too crowded (then (b)). - D12 — Prod load. Options: (a) both countries in full; (b) Attica and Cyprus first. Recommended: (a) if the Greece GeoPackage streams within the loader's memory bound on its new memory-gate test (architecture rule 4); else (b) as the first release and the rest as an ops row. Reversed by: the gate. S1 (2026-10-28): the Greece file is 2.7 GiB against 72 G free on prod; disk does not decide; B17 waits for #1941.
- D13 — Phase shape and the harness. Options: (a) three groups per repo in dependency order (foundation → binding and value → surfaces), one
/phase-build phase-18run cut into segments, the breaker read per segment and the whole-phase rate at every/phase-analyze, with a cut line: a breaker trip after group 2 moves group 3's UI items to Phase 19; B19 and U0 land before the first view issue; (b) one flat group, no cut line. Recommended: (a). Reversed by: the owner (the cut line). The v3 prototype snapshot keeps v2's layout (docs/prototype/v3/source/plusSHA256SUMS), registers its changes underwindow.CL18injs/changes18*.jswith the same call shape asjs/changes17b.js, and numbers ids<view>-<n>continuing each view's v2 series (never<view>-18-<n>, which the coverage script's id pattern cuts); two pre-split harness issues (B19, U0) re-pointscripts/prototype_registry_coverage.py(its globchanges17b*.jsand defaults, lines 46, 75, 179–182) and the UI'srender.sh,render.mjs,prototype-src,states/index.jsonand the README's parity profile at v3. Every UI issue carries its parity box, state file andProver:from the first issue (mid-review F-1, F-2). - D14 — The two open
found-in:phase-17bissues. Options: (a) ui#1249 (engine liveness) and ui#1177 (distance from a place to a live event's hazard footprint, which needs a live-lane route and is not a building outline) go to the Phase 19+ stub; (b) take ui#1177 here. Recommended: (a): neither is property level. Reversed by: the owner adding them as any-time rows.
Market rescan — since 2026-09-26, Phase 17 style¶
Searched 2026-10-28 (web search, the vendors' own pages and trade press). A row with no dated item in
the window says so. Every undated or secondary figure is marked [ASSUMPTION].
| Pace-setter | Shipped since 2026-09-26 (date, source) | Bears on this slice |
|---|---|---|
| Moody's RMS / IRP | Event-response insured-loss estimate for Hurricane Isaias, 16 Oct 2026 (moodys.com); the Australia Cyclone and Flood HD model listed 9 Sep 2026 (moodys.com); HD rebuilds of North Atlantic hurricane and North America earthquake previewed at Exceedance 2026 (moodys.com) | "HD" is the market word for building-resolved loss; nothing new on footprints. Fathom's UK cat model is free on IRP and Oasis LMF since 27 Aug 2026 (fathom.global): the open-engine route of Phase 17 holds |
| Swiss Re CatNet | EigenRisk integrated CatNet hazard data into EigenPrism, 30 Sep 2026 (reinsurancene.ws); CatNet carries a wildfire capability and an updated hail layer (same source) | Hazard-as-data distribution, no building unit; no change to the slice |
| Munich Re LRI | No dated item in the window found. ICEYE's Flood Archive and Flood Early Warning reach LRI's Events feature from January 2026 (article of 9 Dec 2025, insurancebusinessmag.com) | Observed flood per building inside a location-scoring product: the footprint layer is the precondition for joining such observations later (Phase 19+ live lane) |
| Climate X | No item since 2026-09-26 found (searched the press page and trade press on 2026-10-28). Fathom's Global Flood Map is integrated with Spectra, undated (fathom.global) | Spectra and Adapt sell asset-level loss and adaptation ROI; our per-asset curve and value is the same unit |
| Jupiter | Events page lists finalist entries on 21 and 28 Oct 2026 (jupiterintel.com/events); the SEI partnership is older (c. March 2026, sei.org) | Nothing new; building-level resolution is long-standing marketing there |
| Fathom | FathomDEM+ terrain data, 29 Sep 2026, and a white paper on hybrid flood mapping, 5 Oct 2026, both as homepage listings only (fathom.global) [ASSUMPTION: content not opened] |
DEM quality is the lever for building-scale flood; the spike's ≤ 5 m minimum is what the market sells. D4 labels the ≈ 90 m cell rather than pretending |
| ICEYE | Partnership with California's Cal OES, 8 Oct 2026: building-level flood extent and depth, structure-level wildfire damage (iceye.com); OroraTech partnership, 22 Oct 2026, thermal plus radar wildfire lifecycle (newspaceeconomy.ca) | The building is the unit event impact is reported in; a public-sector buyer (Cal OES) bought it. Our footprint layer is what a Phase 19+ feed would join to |
| OS-Climate physrisk | Releases v1.10.2 (2 Oct), v1.10.3 (4 Oct), v1.10.4 and v1.10.5 (14 Oct), v1.10.6 (26 Oct): one damage curve and one disruption curve per event per hazard, aggregation_id on portfolio measures, flood hazards on the JBA global test set (github.com/os-climate/physrisk/releases) [ASSUMPTION: the page prints no year; taken as 2026 since 1.9.4 was May 2026] |
Per-asset damage curves are how the open engine models vulnerability too: D5's rule shape maps onto it if physrisk ever joins as a sidecar |
Regulation (sourced only).
- Greece, Law 5116/2024, Article 5 — companies with annual gross revenue over €2m in the previous
tax year must insure against forest fires, floods and earthquakes from 1 January 2025, for at
least 70 % of the value of the assets; an administrative fine of €10,000, doubled if not remedied
within 30 days (Bernitsas Law briefing, July 2024, read 2026-10-28). Amended by Law 5162/2024:
compulsory for all corporations with gross income of at least €500,000, as well as for all
vehicles (Rokas, Country Update Greece: Insurance 2025, July 2025, read 2026-10-28). The 1 June
2025 effective date of the lowered threshold and the absence of a household mandate come from a
broker guide checked October 2026 (leadinsure.gr)
[ASSUMPTION until the Government Gazette text is read]. What it bears: the insured value is a building value; D3's modelled value and D5's curve are what a pricing read needs. - Greece, ENFIA discount for insured homes (2026) — 20 % where the home's taxable value is at or
under €500,000, 10 % above, pro rata from three months (HAIC, ENFIA Info 2026, February 2026,
eaee.gr, read 2026-10-28).
A minimum insured value of €900 per m² (2024–2026) and €1,000 per m² (2027) is reported by a
broker guide (leadinsure.gr)
[ASSUMPTION: secondary source]; HAIC's 20.8 % of homes insured on 31 March 2026 is cited by the same guide[ASSUMPTION]. What it bears: a sourced €/m² floor would sit beside D3's figure. - EIOPA — the natcat protection-gap dashboard gets a light review in Q4 2026 (Annual Work
Programme 2026, eiopa.europa.eu);
the EIOPA–ESM staff paper on a European risk-sharing mechanism, 9 Apr 2026
(eiopa.europa.eu);
Insurance protection gaps in a changing climate, 16 Apr 2026
(eiopa.europa.eu);
the report on climate impacts on life and health, 30 Sep 2026 (publications page, read 2026-10-28).
No ORSA-specific publication in the window. What it bears: nothing in this slice; the ORSA wording
stays
wording_to_verify(mid-review F-6). - CSRD / ESRS — Commission Delegated Regulation (EU) 2026/1563, the revised ESRS, published in the
Official Journal on 21 Sep 2026 (adopted 3 Jul 2026; in force 10 Nov 2026; mandatory for financial
years from 1 Jan 2027, optional for 2026) (Linklaters,
KPMG,
Crowe); the Omnibus
Directive (EU) 2026/470 in force 18 Mar 2026 (same sources). Whether E1-9's physical-risk financial
effects survive unchanged is reported by vendors only
[ASSUMPTION until read in the OJ text]. What it bears: the Reports templates keep "to verify"; no template work in this phase.
Data sources and licences¶
| Source | Use | Licence | Status |
|---|---|---|---|
| JRC DBSM R2025 (EU Digital Building Stock Model, JRC142133, doi:10.2760/0629989) | The footprint pack: polygons, height or storeys, use, epoch for GRC and CYP | ODbL 1.0 for the database (licenseODbL.txt, JRC142133 § 4.1.1); the Commission's own rights CC BY 4.0 (copyright.txt), both in the R2025 folder |
Resolved at S1: dbsm-greece-R2025.gpkg (2,915,524,608 B, 6,014,199 polygons) and dbsm-cyprus-R2025.gpkg (662,933,504 B, 889,541) with height, use, epoch and floor_area_m2; fill rates and caveats in S1 results |
| A vendored DBSM extract for the smoke seed | tests/fixtures/footprints/ around both demo pins, loaded through the pack from a local path, no network |
ODbL 1.0, redistributed: the fixture README carries the ODbL notice (§ 4.2, § 4.3), the DBSM, OSM and Microsoft attribution and the extract bbox, as tests/fixtures/reference_datasets/README.md does for its upstream |
B5 |
| Overture Maps buildings | Fallback footprints | ODbL ("License for theme: ODbL", attribution page updated 15 May 2026, docs.overturemaps.org/attribution); credit "© OpenStreetMap contributors, Overture Maps Foundation" | Recorded, not loaded (D1) |
| OpenStreetMap, ODbL 1.0 and the OSMF guidelines | The four rules of the licence ADR | ODbL 1.0 | Rule 3 [ASSUMPTION] until counsel ratifies (D2); ADR-089 records the reading; docs/DATA_SOURCES.html lines 128, 503 and 510 defer exactly this review |
| Huizinga, de Moel, Szewczyk 2017, JRC105688 (doi:10.2760/16510) | Maximum damage values for the modelled value (D3) | JRC publication | Resolved at S1: the "building based" €/m² is per m² of footprint and is a depreciated flood maximum (§ 5.3, § 3.3.1.4); D3 uses DBSM's floor area × the construction cost per m² of Table 3-25 instead; the survey area basis is carried as a B9 box |
Eurostat sts_copi_a / sts_copi_q, construction costs of new residential buildings (indic_bt=COST, CPA_F41001_X_410014, 2021 = 100) |
Lifts the 2010 €/m² | Eurostat reuse terms, read and printed at B9 (box) | Picked at S1: 2010 base EL 103.5, CY 92.4; latest EL 129.4 (2026-Q3), CY 120.3 (2026-Q2) |
ESRM20 vulnerability classes (in repo, scripts/packs/esrm20_2021_pga_vulnerability.json) |
Earthquake curve binding by epoch and height | As recorded in docs/DATA_SOURCES.html |
In place |
| ENFIA insured-value floor (€/m²) | A sourced line beside the modelled value | Greek ministerial decision, to read | [ASSUMPTION] until read; not a build dependency |
| Mapillary | Street photos | CC BY-SA | Not used (D8) |
| GADM 4.1, geoBoundaries | Unchanged | Unchanged | — |
Every [ASSUMPTION] above is either resolved at S1 or the named issue before a figure ships, or
listed here as not a dependency.
Architecture¶
- Layers (CLAUDE.md).
api/→core/→db/repositories/; the worker reaches the rest only through the queue and the shared DB. The footprint layer is a new reference tablebuilding_footprintswith noorg_idand no row-level security, likeadmin_boundaries(models/admin_boundary.py:MULTIPOLYGON 4326, a GIST index, a bbox-required and zoom-simplified route):source,license,attribution,country,height_m,storeys,use,epoch,area_m2,modelled_value,value_method.exposures.footprint_idis a nullable indexed reference (migration, architecture rule 5).home_editsandimpact_function_bindingsare org-scoped tenant tables withFORCE ROW LEVEL SECURITYand theorg_isolationNULLIF policy (the app roleclimate_lama_appisNOBYPASSRLS, CLAUDE.md "Gotchas"); the policy count attests/db/test_app_role_rls_isolation.pyline 544 rises, both tables join_ORPHANED_ORG_SCOPED_TABLES, and atests/test_db/assertion proves it wheremigrations-gate.ymlruns (it runstests/test_db/only, lines 150–155). - The pack loads on the maintenance lane (rule 4). The ingest pipeline is raster-window only
(
worker/ingest/plan_chunks.py,write_chunk.py) and itsingest_jobsandingest_chunksare org-scoped (docs/DEPLOYMENT.mdline 895), while the footprint layer has no org. Soscripts/packs/building_footprints.pyis a chunked script loader in the shape ofscripts/load_admin_boundaries.py, run as the owner or maintenance DSN of the lane table indocs/DEPLOYMENT.md(lines 612–643): a vector reader (pyogrio.read_arrowby feature offset or bbox chunks) staged from MinIO,COPYinto the footprint table one chunk in memory at a time,dataset_identity.pyfor the identity, and a new memory-gate test beside the raster gate (tests/worker/ingest/test_memory_regression_gate.pycovers the raster mode only) on a country-scale synthetic GeoPackage. - Compute (CLIMADA isolation, ADR-024, ADR-042, ADR-084). Nothing new imports an engine. Curve
binding resolves in
coreinto the per-pointimpf_idarray; the worker loads every resolved curve and the adapter builds theImpactFuncSetof all of them (D5), the CLIMADA sidecar takesimpf_per exposure as it does today, and the Oasis sidecar takes a list of curves (docker/oasis-sidecar/model_builder.pyholds one curve today at lines 690–740: one intensity bin scheme from the largestx[-1], one shared damage-bin table from the union of every curve's rounded MDDs, one vulnerability row set per curve with its own id against the shared bins, the single-curve check at line 735 replaced by an unknown-id check;keys.csvalready writes aVulnerabilityIDper item at lines 427–431), with a two-curve reference case beside the one-curve conformance scenario. No engine release is needed; the agreement script gains a bound-curve case. - Cache and provenance. The result cache key gains the binding hash (the resolved per-point map) and, for the money line, the edits digest through the "homes" row's sha; provenance records the curve list (D5); a rule edit misses the cache instead of serving stale figures.
- Long compute on the queue (rule 2). The snap job, the modelled-value pass and the money line are jobs; the API returns ids and the UI polls as it does for every run.
- Tiles (ADR-083). Footprints are an MVT source of their own from z 14; the result and exposure
tiles gain a
buildingslayer from z 15 by bindingfootprint_idper asset and left-joining the footprint table (D10). Tiles are cached in MinIO. - The run contract (ADR-087). Amended so an impact compute accepts
exposure=building:<id>: one point at the footprint with the value fromhome_edits, else the modelled value labelled ODbL (B18); the "homes" dataset row is the materialisation (D6, D7). - Config (rule 6).
CL_FOOTPRINT_SOURCE, the snap distance, the modelled-value index and the per-building export switch (off by default, D2) come from settings with defaults. - No demo data (ADR-077). The smoke seed's footprints are a vendored real DBSM extract with its
notice, loaded through the pack from a local path (
scripts/smoke_e2e.pyloads its other real fixtures the same way, lines 172–194 and 258–260); the prototype's figures never enter the product. - Errors (rule 7) and clients. New codes in
core/exceptions.py:E_FOOTPRINT_NOT_FOUND,E_FOOTPRINT_LICENCE,E_BINDING_NO_CURVE; the SDK regenerates (sdk-regenerate gate) and every new route gains its request intests/climate-lama.postman_collection.json(scripts/demo.pyno longer exists, so that half of the rule is moot).
Guardrails — every Phase 18 issue (the Phase 17b lessons)¶
Phase 17's guardrails 1 to 8 and Phase 17b's 1 to 12 apply. Plus, from the mid-review and the reassess pass:
- The design is the test from the first issue (F-1). Every UI issue carries its registry ids
(
<view>-<n>, D13), its parity box against the v3 prototype captures, its state file and itsDesign:line at split time; the builder reads the captures before editing and the PR's visual captures before merge. - The breaker is read twice (F-2). Per segment by
/phase-build, and whole-phase at every/phase-analyze; a trip stops the lane until its repair set lands, and D13's cut line applies. - No vacuous layout proof. Every layout assertion measures the view root and records its prove-it-fails run; the 390 px check covers Lookup's building card and Edit home.
- Prod-shaped fixtures. The seed holds the vendored real footprints around both demo pins
with their notice, an exposure dataset with
occupancy,storeysandepoch, a member with no value (modelled), a member with no footprint within the snap distance, and every fixture currency and unit prod holds. - A
Prover:on every stack box.e2e-smoke.yml <spec>,e2e-visual.yml parity <view>,sdk-smoke.yml,migrations-gate.yml(with the box's assertion undertests/test_db/),oasis-conformance.yml, the memory gate,performance.yml, orneeds-prod-proofat the release. No box without one leaves the split. - Licence before data. No footprint row reaches prod before ADR-089 is merged; a guard test
proves zero tenant rows in the footprint layer, no
org_idon it, the credit on every map and PDF, and that every result and exposure GeoJSON and every per-building export emits onlyPointgeometry at the tenant's own coordinates with no footprint attribute butfootprint_id. - Honest cells and honest ownership. Every per-building figure carries
cell_size_mand the grade; no per-building flood figure ships without the ≈ 90 m line (D4); no non-flood per-building figure above screening grade (D9); every figure derived from footprint attributes carrieslicense="ODbL-1.0"and never enters a tenant-private export (D2). - Tenant isolation on the new tables.
home_editsandimpact_function_bindingsship with forced RLS and the policy; a cross-org read returns zero rows; tenant B's Lookup never serves a figure computed from tenant A's edits (B13). - Every new route updates the SDK and the Postman collection (B3, B4, B6, B12–B15, B18).
- Regulatory wording stays "to verify" (F-6); no legal figure without its source.
Scope — groups and items¶
Each item names its grounding and its prover. Estimates are the split's to confirm.
Backbone — CortoMaltese3/climate-lama¶
| Group | Id | Item | Grounding | Prover |
|---|---|---|---|---|
| B-0 Pre-split | B19 | scripts/prototype_registry_coverage.py reads v3: the glob changes1*.js (or --registry-glob), defaults --registry-dir …/v3/source/js and --phase-doc docs/plan/phase-18-property-level.md; unmapped: empty over the v3 registry before the split |
the script's lines 46, 75, 179–182 | ci.yml |
| B-A Foundations | B1 | ADR-089, the licence review docs/DATA_SOURCES.html line 128 defers to: rules 1, 2 and 4 decided, rule 3 recorded pending counsel, the § 10 row; the guard test of guardrail 6 |
docs/DECISIONS.md ends at ADR-088; docs/DATA_SOURCES.html:128, 503, 510 |
ci.yml |
| B-A | B2 | building_footprints model, migration (no org_id, no RLS, MULTIPOLYGON 4326, GIST) and repository; the loader scripts/packs/building_footprints.py on the maintenance lane (pyogrio chunks, COPY, per-artefact licence and attribution, dataset_identity); the lane row in docs/DEPLOYMENT.md; a memory-gate test on a synthetic country-scale GeoPackage; country profile rows |
models/admin_boundary.py, scripts/load_admin_boundaries.py, docs/DEPLOYMENT.md:612–643, 895 |
migrations-gate.yml, the new memory gate |
| B-A | B3 | Footprint MVT GET /v1/tiles/footprints/{z}/{x}/{y} from z 14 with attributes, cached; Postman |
api/v1/tiles.py, core/exposure_tiles.py |
sdk-smoke.yml (a tile answers 200 on the seed) |
| B-A | B4 | exposures.footprint_id migration (nullable, indexed); POST /v1/exposures/{id}/snap-footprints job (contains, else nearest within the snap distance, else none) with a coverage figure on the dataset read and the opt-in "fill attributes from the footprint" flag; Postman |
models/exposure.py:191, core/exposure_upload_job.py |
migrations-gate.yml, sdk-smoke.yml |
| B-A | B5 | Smoke seed: tests/fixtures/footprints/ (the vendored DBSM extract around both demo pins, its README with the ODbL notice, attribution and bbox) loaded through the pack from the local path; an exposure dataset with occupancy, storeys, epoch; a valueless member; an unsnappable member |
scripts/smoke_e2e.py:172–194, 258–260, the UI seed |
sdk-smoke.yml, e2e-smoke.yml seed.spec.ts |
| B-B Binding and value | B6 | Curve-binding rules: impact_function_bindings (org-scoped, forced RLS, the policy, the count at test_app_role_rls_isolation.py:544, _ORPHANED_ORG_SCOPED_TABLES, a tests/test_db/ assertion), CRUD GET/POST/PATCH /v1/impact-function-bindings, seeded defaults keyed to the built-in curve identity and resolved per org, lazily beside seed_builtin_impact_functions (a fresh org's first bound compute succeeds); Postman |
models/impact_function.py:13–14, 39, 80, core/impact_function_seeder.py, api/v1/impact_functions.py:14 |
migrations-gate.yml, ci.yml |
| B-B | B7 | Compute applies bindings: the resolver (tenant attributes first, footprint attributes where opted in, unknown values → the default curve with a warning), every resolved curve loaded with a small integer id, the per-point impf_id filled (replacing worker/tasks.py:1970), the adapter's ImpactFuncSet of all curves at its four call sites, measures adapting every function, the provenance curve list, the binding hash in the cache key; the result's per-asset curve and license on the asset read |
engine_adapter.py:27, 305, 472–502, 553, 561–574, 673; worker/tasks.py:274, 296, 1970, 1994–2000, 2135; core/result_cache_service.py:18–19, 123–125 |
sdk-smoke.yml (two members in one cell with different occupancy get different losses) |
| B-B | B8 | Oasis sidecar: a curve list in the payload (one-element list kept for the old shape), one bin scheme from the largest x[-1], the shared damage-bin table, per-curve vulnerability ids, the unknown-id check; a two-curve case in the conformance reference; the adapter sends the list |
docker/oasis-sidecar/model_builder.py:427–431, 663–664, 690, 709–711, 735–740, worker/models/oasis_adapter.py |
oasis-conformance.yml |
| B-B | B9 | Modelled building value on the footprint row (area × storeys × €/m², D3), value_source on the exposure and license="ODbL-1.0" where it applies, the method page, the ENFIA line when sourced |
core/exposure_service.py:54, 363–376 |
ci.yml; the method page in mkdocs build --strict |
| B-B | B10 | The buildings layer of result and exposure tiles from z 15: footprint_id per asset bound as one more array in core/result_asset_tiles.select_points, the left join in the MVT SQL, the exposure points SQL joined the same way |
db/repositories/vector_tile_repository.py:280–310, 352–371, models/result.py:62–67, ADR-083 |
performance.yml with a z 15 entry in tile-latency.spec.ts (U0) |
| B-B | B11 | Agreement script: a bound-curve portfolio through the three engines | scripts/verify_engine_agreement.py |
sdk-smoke.yml |
| B-C Lookup, reports, ops | B12 | Lookup building block: footprint id and attributes, cell_size_m per hazard from native_resolution (null → "resolution unknown"), the H3 resolution kept apart, grade, the modelled value and its licence; Postman |
core/lookup_service.py:1650, models/hazard.py:223–226 |
sdk-smoke.yml |
| B-C | B13 | The money line: the per-org "homes" system exposure dataset (one row per saved home, materialised from home_edits else the modelled value), the one-row compute through POST /v1/compute/impact, the org-scoped impact_results row the cache anchors on, the edits digest in the key, served on the next read; tenant B never reads tenant A's figure; Postman |
core/compute_service.py:419, 1038–1046, core/result_cache_service.py:216–241, worker/tasks.py:1587–1615, 1771, 5302–5307 |
sdk-smoke.yml, a tests/test_db/ isolation case |
| B-C | B14 | Home edits: home_edits (org-scoped, forced RLS, the policy, the count, the orphan list, a tests/test_db/ assertion) and PUT /v1/lookup/homes/{footprint_id} (floors, epoch, value, use), tenant-private, read by the lookup and materialised into the "homes" row; Postman |
— (new; exposures as the RLS precedent, vector_tile_repository.py:27) |
migrations-gate.yml, sdk-smoke.yml |
| B-C | B15 | Attribution and exports: license on every per-building API read; the per-building export (tenant point + footprint_id only) behind the setting off by default; the PDF credit with openstreetmap.org/copyright and the DBSM credit; the geometry guard of guardrail 6 |
core/reports/, api/v1/results.py:196–258, api/v1/exposures.py:640 |
ci.yml (the PDF contains the URL string) |
| B-C | B16 | Portfolio upload: occupancy, storeys, epoch typed and validated into properties by the resolver, the snap on upload, the opt-in flag, coverage on the rollup (the CSV template is the UI's static asset, U7) |
core/exposure_service.py:54, 363–376, 519, 595–596, 674, core/exposure_upload_job.py |
sdk-smoke.yml |
| B-C | B17 | Ops: load DBSM R2025 Greece and Cyprus on prod through the lane (D12), record sizes, durations and disk | docs/DEPLOYMENT.md |
needs-prod-proof at the first release |
| B-C | B18 | The run contract takes a building: ADR-087 amended, exposure=building:<footprint_id> accepted by impact compute as one point with the value from home_edits else the modelled value labelled ODbL, through the "homes" row; set_scope parity on the assistant's tool schema; Postman |
core/compute_service.py, ADR-087, core/assistant/ |
sdk-smoke.yml, e2e-smoke.yml assistant-run.spec.ts |
UI — CortoMaltese3/climate-lama-ui¶
Registry ids are the v3 registry's (js/changes18.js, window.CL18), in the <view>-<n> shape
continuing each view's v2 series (D13); MapKit changes continue v2's shell-m<n> series.
| Group | Id | Item | Registry ids (v3) | Prover |
|---|---|---|---|---|
| U-0 Pre-split | U0 | The parity harness at v3: render.sh takes the snapshot version (default v3) for the SHA256SUMS path, prototype-src re-copied from v3/source, render.mjs re-pinned, states/index.json with every new id → {hash, file} (data-18 and data-19 from #data.buildings; lookup-17 to lookup-19, explore-46 and portfolios-16 through a state file with a prepare step, as the 17b override and switch entries are; lookup-20 as the view-level lookup-390 capture), the README's parity profile rows at v3, a z 15 entry in tests/e2e/performance/tile-latency.spec.ts; the v3 captures rendered per registry id (Deps: ui#1296, the seed rows the visual stack's state captures need) |
tests/e2e/parity/render.sh:38, 42, render.mjs:1–3, docs/prototype/README.md:130–135, tile-latency.spec.ts:20 |
e2e-visual.yml, performance.yml |
| U-A Foundations | U1 | MapKit footprint source from z 14 with the ODbL corner credit; outline on hover and select; the buildings layer of the result and exposure layers from z 15 |
shell-m4, shell-m5 |
e2e-visual.yml parity explore, e2e-smoke.yml |
| U-A | U2 | Data: the footprint pack card (licence, attribution, version, coverage per country); the damage-curve rules table, org-admin editable | data-18, data-19 |
e2e-visual.yml parity data |
| U-B Lookup | U3 | The building card (V1): attributes, the cell-size line per hazard, the "modelled" badge and the "ODbL figure" tag, the money line in its three states (none, computing, figure) | lookup-15, lookup-16, lookup-17 |
e2e-visual.yml parity lookup, e2e-smoke.yml lookup-building.spec.ts |
| U-B | U4 | Edit home: floors, epoch, value, use; recompute; the tenant-private note; reset | lookup-18 |
e2e-smoke.yml lookup-building.spec.ts |
| U-B | U5 | The place report's building section; the 390 px check of the building card and Edit home | lookup-19, lookup-20 |
e2e-visual.yml parity lookup@390 |
| U-C Explore, Portfolios, Reports, Home | U6 | Explore: one building as the exposure through "My assets" (building in scopeKinds.ts, runAddress.ts parse and format, set_scope taking exposure=building:<id>, the registry guard test); the asset card names the bound curve and why |
explore-45, explore-46 |
e2e-visual.yml parity explore, e2e-smoke.yml |
| U-C | U7 | Portfolios: the static upload template gains occupancy, storeys, epoch; the Snap to buildings step with the coverage meter and the opt-in attribute fill (V3); member rows with footprint, curve and the ODbL tag; the "modelled" badge |
portfolios-16, portfolios-17, portfolios-18 |
e2e-visual.yml parity portfolios, e2e-smoke.yml portfolio-upload.spec.ts |
| U-C | U8 | Reports: the credit in the preview; the per-building export control, off until the licence review | reports-15 |
e2e-visual.yml parity reports |
| U-C | U9 | Home: the area card counts footprints ("Buildings in data") | home-16 |
e2e-visual.yml parity home |
Engine — CortoMaltese3/climate-lama-engine¶
None. The per-point impact-function id is in the engine's exposure contract already; D5 binds in the backbone and the worker builds the multi-curve set. Recorded so the split does not look for an engine wave.
Spikes, operations and closeout¶
- S1 (before the split, one day): confirm the DBSM R2025 Greece and Cyprus files, sizes and
attributes (D1, D12), read Huizinga § 2.1 for the basis (D3), pick the construction-cost index.
Output: the three
[ASSUMPTION]s resolved in this file. Run 2026-10-28; results in S1 results below. - O1 (owner, any time): counsel's reading of rule 3 (D2). Not a gate; the export switch stays off until then.
- O2 (owner, any time): the FEK text of Law 5116/2024 as amended, and the ENFIA €/m² decision.
- R1: release after groups B-A, B-B, B-C and U-B land (Moment 1 on prod); R2: after U-C (Moment 2).
- Closeout:
/parity-audit phase-18,mid-review-phase-18.md, the plan.md banner.
S1 results (2026-10-28)¶
Read from this machine on 2026-10-28 between 08:05Z and 08:40Z. The GeoPackages were read over HTTP
range requests (GDAL /vsicurl through pyogrio 0.13 / GDAL 3.12.4), with no local copy. Prod was
read over SSH, read-only. The S1 outcome: D1 stands at (a), because both files exist and every
attribute D5 needs is present (see the fill rates for how coarse each one is). D3's reversal
clause applies: Huizinga's per-m² figure is per m² of footprint, so the modelled value uses
DBSM's floor area instead (below). D12 stands at (a) on disk: the loader's memory gate still
decides. Every [ASSUMPTION] S1 was asked to resolve is resolved, or carried below as a named box
on B2 or B9.
1. DBSM R2025 files (D1, D12). The folder
https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/DBSM/DBSM_Europe_R2025/per-country/ lists 27
per-country GeoPackages dated 2025-12-12, Greece and Cyprus among them. The folder above it holds
licenseODbL.txt (2025-05-06), copyright.txt (2026-03-05) and Changelog_DBSMR2025.txt
(2026-01-12).
dbsm-greece-R2025.gpkg |
dbsm-cyprus-R2025.gpkg |
|
|---|---|---|
Size (HTTP Content-Length) |
2,915,524,608 B (2.7 GiB) | 662,933,504 B (632 MiB) |
| Layer, geometry, CRS | one layer dbsm-greece-R2025, Polygon, EPSG:3035 |
one layer dbsm-cyprus-R2025, Polygon, EPSG:3035 |
Features (gpkg_ogr_contents) |
6,014,199 | 889,541 (the report's Table 4 gives the same total: 460,299 EUBUCCO, 75,245 OSM, 353,997 Microsoft) |
Both files carry the same 13 fields: unique_id, source (eub, osm, msb), area (footprint
m²), height (m), shapefactor (m²/m³), epoch (integer), use (integer), floor_area_m2,
eub_data, osm_data, msb_data (text), rooftop_pv_potential_MWh_per_y and pv_data. The
codes come from JRC142133's attribute table (PDF p. 12):
epoch: 0 no data, 1 before 1980, 2 1980–1990, 3 1990–2000, 4 2000–2010, 5 2010–2020.use: 0 no use assigned, 1 residential, 2 non-residential.
floor_area_m2 is "the product of the footprint area multiplied by the height and divided by 3"
(changelog, entry of 24/11/2025).
Fill rates, from samples of 10,000 rows read over HTTP range requests (no local copy):
| Cyprus sample (offset) | Source mix | height filled |
height = 2.5 m |
epoch 0 (no data) |
use 0 (none) |
|---|---|---|---|---|---|
| 0 | — (not counted) | 64.8 % | — | — | — |
| 300,000 | — (not counted) | 66.2 % | — | — | — |
| 600,000 | eub 45.8 %, msb 49.1 %, osm 5.1 % | 43.1 % | 99.5 % of filled (4,288 / 4,308) | 57.1 % | 57.1 % |
At offset 600,000 the filled bins split as follows. epoch: 1 = 29.8 %, 2 = 3.6 %, 3 = 5.1 %,
4 = 2.9 %, 5 = 1.5 %. use: 1 = 42.7 %, 2 = 0.15 %. The rows with epoch 0 and use 0 are the
same 5,713, which are the rows with no GHS cell attached. The first two samples were run before the
probe counted values per bin, so they report fill rates only.
The two signals that matter for D5 and B2 are these. Where height is filled, it sits almost
entirely on the 2.5 m floor. More than half the rows in that sample carry no epoch and no use.
B2's encoding box therefore needs a no-data path for both: a 2.5 m height and use 0 are defaults,
not observations, and B2 must handle them as defaults.
Open — resume after the 2026-10-28 reboot (S1-fill). The Cyprus sample at offset 880,000 and
the five Greece samples (offsets 0 %, 25 %, 50 %, 75 % and 99 % of 6,014,199 features) were not
run. They were stopped for the C: volume repair, and the 880,000 sample had stalled with no
output. To resume, run dbsm_probe.py <url> sample <offset> 10000 per offset, then fill this
table. The probe is the S1 one-off (pyogrio over /vsicurl/). The split commits it under
scripts/ if B2 needs it again. Until those samples land, Greece's fill rates are unmeasured.
D1's reversal is not triggered on the evidence so far: every attribute D1 needs exists in both
files.
What the attributes are, which D5 and the method page must say:
- Height and storeys. There is no storeys field. Height comes from Urban Atlas Building Height
2012 where a core city has it, else from GHS-BUILT-H at 100 m (JRC142133 § 5.1). The floor of the
distribution is 2.5 m. B2 derives storeys from height with DBSM's own convention, height ÷ 3,
which is the one
floor_area_m2uses. - Epoch. The epoch is a 100 m-cell proxy from GHS-AGE: "the construction year of the predominant buildings within the area" (§ 5, PDF p. 34). It is not the age of the building. Its newest bin is 2010–2020.
- Use. Use is binary, derived from GHS-BUILT-C and GHS-BUILT-S. D5's occupancy bands can
therefore read only residential and non-residential from the footprint. Anything finer comes from
the tenant's own
occupancy. - CRS. The CRS is EPSG:3035, so B2's loader reprojects to the table's
MULTIPOLYGON 4326and promotesPolygontoMULTIPOLYGON. - Licence.
licenseODbL.txtand the report (§ 4.1.1, PDF p. 15, "distributed under ODbL-1.0 license") put the database under ODbL 1.0.copyright.txtadds that the Commission's own copyright and sui generis right are under CC BY 4.0. The credit therefore names both: ODbL for the database (OSM, Microsoft and EUBUCCO contributors) and "© European Union, 1995–2026" for the JRC part.
D1's reversal rule does not fire: no file is missing, and Overture (option c) has no epoch or use to add.
2. Huizinga 2017 basis (D3). Read from the report PDF and its spreadsheet in the JRC repository
(repository/handle/JRC105688). § 2.1 (PDF p. 9) is the methods overview and does not state the
basis. The basis is stated in § 5.3 (report p. 71, PDF p. 76): the "building based" value is
"maximum damage per square meter for buildings if the footprint of individual buildings is used for
damage calculation". The land-use-based value assumes 20 % footprint density for residential and
30 % for commercial and industrial. The object-based value assumes a 100 m² residential footprint
per object.
The €/m² is per m² of footprint. It is also a flood maximum damage, not a building value. The spreadsheet builds it in two steps:
- The construction cost per m² comes from the power law of Table 3-25 (
y = 24.1 · GDPpc^0.385for residential). The construction-cost surveys are weighted by their share of floor space (§ 3.3.1.2). - The structure value is that cost × 0.6, the depreciated value (§ 3.3.1.4), × (1 − 0.4), the part of a concrete or masonry building a flood never damages (MaxDamage-Adjustment tab). Contents add 50 % for residential.
| Country (2010 €/m², footprint) | Residential structure | + contents | Commercial structure | Industrial structure | Implied construction cost (structure ÷ 0.36) |
|---|---|---|---|---|---|
| Greece | 440.58 | 660.87 | 463.61 | 304.94 | 1,223.8 residential (the power law at GDPpc 26,861 US$ gives 1,222.5) |
| Cyprus | 447.00 | 670.49 | 469.87 | 308.68 | 1,241.7 residential (the power law at GDPpc 27,889 US$ gives 1,240.2) |
D3's reversal clause therefore applies. Multiplying the footprint figure by storeys would count the building's floors twice, and treating a depreciated, partly undamageable flood maximum as a value would understate it. The modelled value B9 ships is:
floor_area_m2 (DBSM) × the construction cost per m² of floor space (Table 3-25's power law for the
class) × the Eurostat lift below
It is labelled "modelled", is a replacement value with no depreciation, and carries the method page.
use 1 takes the residential law, use 2 the commercial law, and use 0 the residential law with
a warning. Huizinga's flood maximum stays what the flood curve multiplies (D5), not the value.
3. Construction-cost index 2010 → 2026 (D3). Eurostat sts_copi_a (annual) and sts_copi_q
(quarterly), "Construction producer prices or costs, new residential buildings", indic_bt=COST,
cpa2_1=CPA_F41001_X_410014 (residential buildings, except residences for communities),
unit=I21 (2021 = 100), s_adj=NSA. Read through the Eurostat dissemination API on 2026-10-28;
the annual table was updated 2026-09-26 and the quarterly 2026-10-24.
| 2010 (annual) | 2025 (annual) | Latest quarter | Lift to 2025 | Lift to latest quarter | |
|---|---|---|---|---|---|
Greece (EL) |
103.5 | 123.1 | 129.4 (2026-Q3) | 1.189 | 1.250 |
Cyprus (CY) |
92.4 | 118.0 | 120.3 (2026-Q2) | 1.277 | 1.302 |
Chosen: this index, the 2010 annual value as the base and the latest published quarter as the numerator, recorded on the method page with its period. Eurostat publishes it for new residential buildings only, so non-residential values use the same lift, and the method page says so.
4. Prod disk against the Greece file (D12). Read over SSH at 2026-10-28T08:17:47Z:
- Disk.
/(and/var/lib/docker) is 150 G ext4: 73 G used, 72 G available, 51 % used. - Docker. Images take 35.9 GB (31.1 GB reclaimable), the build cache 22.7 GB (19.1 GB
reclaimable) and local volumes 21.3 GB, of which
climate-lama_postgres_datais 9.3 G. - Memory. RAM is 15 Gi with 7.3 Gi available. Swap is 4 Gi with 2.8 Gi in use.
Staging both GeoPackages in MinIO takes 3.6 GB, and the loaded table is measured at B17. Disk does not decide D12. The loader's memory gate does, as D12 already says. Swap was 70 % used at the read, so the gate's bound must hold with the stack's other services running.
Carried as named boxes (each has no prover yet; the split writes them into the issue):
- B2: a box: "the credit carries both the ODbL 1.0 database attribution and '© European Union,
1995–2026, CC BY 4.0' for the JRC part (
copyright.txtof the R2025 folder)". - B2: a box: "storeys = max(1, round(height ÷ 3)), the convention of DBSM's
floor_area_m2;epochstored as DBSM's bin code with 0 as null; auseof 0 stored as null". - B9: a box: "the floor-area basis of the construction-cost surveys behind Table 3-25 (EC Harris
2010, Turner & Townsend 2013) is read and cited on the method page". Huizinga gives their figures
"per m2" and weights them by floor space (§ 3.3.1.1–3.3.1.2) but does not name the area
definition, so it stays
[ASSUMPTION: gross floor area]until read. - B9: a box: "the Eurostat reuse and credit terms for
sts_copiare read and printed on the method page". - B17: depends on #1941 (prod Postgres segfaults on geometry queries, filed 2026-10-28 on the Phase 17b walk-corrections milestone). Six million polygons should not be loaded into a server that crashes on simplify and clip until that issue is closed.
Exit criteria (tied to showcase moments)¶
- Moment 1, walked on prod: at the Greek pin
40.60, 22.80and the Cyprus pin34.89, 33.61Lookup shows the building the pin falls in (or "no building within N m"), its storeys, use and epoch, a cell-size line per hazard in metres, a modelled value marked "modelled" with its ODbL tag, and a money line that changes after Edit home; the place report prints the building and the ODbL credit; both at 390 px. Prover before the walk:e2e-smoke.yml lookup-building.spec.ts. - Moment 2, walked on prod: a portfolio uploaded with
occupancy,storeysandepochsnaps to footprints with its coverage figure; two members in one hazard cell with different occupancy show different losses; a member whose curve came from the footprint carries the ODbL tag and a member with tenant attributes does not; the book's map draws footprints coloured by loss at z 15; the portfolio report carries the credit. Prover:sdk-smoke.ymlande2e-smoke.yml portfolio-upload.spec.ts. - Moment 3: "Compare engines" on the bound portfolio agrees within the tolerance of
verify_engine_agreement.pyacross climate_lama, climada and oasis, and the two-curve conformance case passes. Prover:sdk-smoke.yml,oasis-conformance.yml. - Demo slice unchanged and green (
scripts/smoke_e2e.py). - Licence: ADR-089 merged before the first footprint row on prod; the guard test green; the credit on every footprint map and PDF; the export switch off on prod until O1.
- Measured: p95 first tile at z 15 under 500 ms on the performance suite's new entry; the loader's memory gate green; every layout assertion on the view root with its prove-it-fails run.
- Tenant isolation: the RLS assertions on
home_editsandimpact_function_bindingsgreen undermigrations-gate.yml; the cross-org money-line case green. - Guardrails and the breaker: the whole-phase escape rate reported in
mid-review-phase-18.mdwhatever its value, every trip with its repair set;/parity-audit phase-18filed. - Record:
mid-review-phase-18.md, the plan.md banner, ADR-089, the ADR-087 amendment, the Phase 19+ stub carried.
Split readiness (for /phase-split phase-18)¶
- Phase 17b's repair set lands before any Phase 18 UI view issue (amended 2026-10-28, after the
production parity walk). The set is now three parts, all on the "Phase 17b -- Walk corrections"
milestone with
found-in:phase-17b, so/phase-build phase-17bpicks them up: - The CI
/parity-audit phase-17bof 2026-10-28 (E2E Visual run 40593592450 on UIab69f14): ui#1285 to ui#1295, 21 parity mismatches on eleven views, and ui#1296, the visual stack lacking theseed.spec.tsrows that 30 state captures need. - The production parity walk of 2026-10-28 (prod, UI
v0.21.0, backbonev0.31.0; summary in the second comment on ui#1284, 01:05Z): 228 items, 87 mismatches of which 19 are blockers. It filed ui#1307 to ui#1322, one issue per view (adapt, admin, analyses ×2, assistant, data, events, explore ×2, home, lookup, portfolios, reports ×2, settings, shell), alongside the app-wide token issue ui#1299 and the visual-harness issue ui#1302. - The backbone issues for the walk's backbone-caused boxes, filed by the sibling backbone session
on the same milestone: #1938 (assistant run recipes → ui#1311), #1939 (late and failed feeds in
/v1/admin/failures→ ui#1308), #1940 (reactivating a superseded exposure version, the 409's watch list → ui#1312), #1941 (prod Postgres segfaults on admin-boundary geometry queries), #1942 (retrying a job's failure write during crash recovery), #1943 (503E_SERVICE_BUSYinstead of 500 while the database is unavailable). Read 2026-10-28 08:20Z fromgh; the sibling may file more, and every one it files on that milestone belongs to the set.
Phase 17 and 17b close at 0 open issues on that milestone.
- The UI breaker is tripped. The UI escape rate over the Phase 17b window is over the 0.25
breaker: the sibling sessions' read of 2026-10-28 gives 0.41, and this session's
python scripts/rework_rate.py --repo CortoMaltese3/climate-lama-ui --since 2026-10-02 --follow-ups --json
at 08:18Z reads 0.42 (42 escapes in 101 builds, 26 harness fixes not counted, partial 0.03,
coverage 0.77), the same figure as mid-review-phase-17.md F-2. Either way it is tripped, and the
walk's repair set is expected to keep it tripped until it lands. Under D13's cut line, every Phase 18 UI view issue
(U1 to U9) waits for the repair set above: Lookup, Explore, Portfolios, Reports, Data and Home
must match the v2 design before U-A to U-C build on them, and U0's capture index needs ui#1296's
seed rows and ui#1302's harness fix. The backbone is not held: groups B-0, B-A and B-B (B19,
B1 to B11) may dispatch as soon as the owner locks this plan and /phase-split phase-18 has run.
B-C waits only on its own dependencies (B12 to B18 on B-A and B-B), not on the UI set. If the
UI breaker still reads tripped when B-B lands, D13's cut line moves U-C to Phase 19.
- Every item above has a Grounding: path or is marked new, a Prover: and (UI) its registry ids.
- B19 and U0 land first: the v3 captures exist per registry id before the first view issue is
written, and scripts/prototype_registry_coverage.py prints unmapped: empty over the v3
registry against this document.
- The Direction board's picks are read (ArtifactData list picks) and the Confirmed column filled.
- S1's three [ASSUMPTION]s are resolved or carried as named boxes on B2, B9. Done 2026-10-28, except the fill-rate samples, which are left open as S1-fill. They need not block the lock, but their results go into B2's encoding box.
(S1 results). The split writes the four named boxes into B2 and B9, and
the #1941 dependency into B17's Deps:.
- The Direction board's picks collection read empty on 2026-10-28 (07:30Z, then by this
session at about 08:06Z and 09:38Z): no owner pick yet, so Confirmed stays blank until the lock.
- Conflict hotspots: migrations/versions head chain (B2, B4, B6, B14 — serial in merge order);
api/v1/tiles.py and db/repositories/vector_tile_repository.py (B3 then B10);
core/lookup_service.py (B12 then B13, B14); core/exposure_service.py (B9 then B16);
worker/tasks.py and engine_adapter.py (B7 then B8, B13, B18 — serial); core/compute_service.py
(B13 then B18); tests/climate-lama.postman_collection.json (every route issue, serial); UI
src/map/MapKit* (U1 before U6, U7); src/components/lookup/* (U3 → U4 → U5, one owner at a
time); src/actions/explore/setScope.ts, src/run/runAddress.ts, scopeKinds.ts (U6 only);
tests/e2e/smoke/contract-map.ts (every consumer, serial).
Phase 19+ stub¶
Everything in the Phase 18 charter stub that this phase does not take, with its source line. plan.md's stub section stays as the index; this list is what remains.
| Item | Source line |
|---|---|
| Engines beyond Oasis: Delft-FIAT, OS-Climate physrisk, OpenQuake (after its AGPL review), uncertainty ranges | D19, phase-17-revamp.md "Out of scope"; exploration/06-openquake-scoping.md |
| Oasis OED per-location and per-account policy terms, layers and reinsurance | D19 |
| AEP on stochastic event sets; stochastic event sets themselves | D19, D21 |
| GeoTIFF exposure upload through the API | D21 |
| Polygons and corridors as exposures (roads, railways, parks); footprints are polygons in Phase 18, corridors are not | D17 |
| Street photos (user-uploaded first; Mapillary needs a share-alike review) | property-level § 5 item 9; D8 above |
| Building-scale (≤ 5 m) flood hazard sourcing for Greece and Cyprus | property-level § 5 item 6; D4 above |
| Spatial exposure growth (new locations entering hazard zones) | run model § 13 |
Vulnerability curves for population and built-volume exposures (persons, m3 stay score-only) |
run model § 13, domain model § 9 |
An exposure reference_year column |
run model § 13 |
| A multi-hazard aggregate figure | run model § 13 |
| Translations (strings, locale formats, a language preference) | run model § 13 |
| Assistant providers beyond Anthropic and OpenAI | run model § 13 |
| Loss-threshold notifications in the live lane (needs an ADR-078 item 6 amendment, K28) | domain model § 9 |
Appraisal extras: risk_rp_100, per-year discount schedules, portfolio risk transfer, measure entity swaps |
domain model § 9 |
| Live-lane science: probabilistic or ensemble footprints, uncertainty on the event loss, a flood feed, a forecast lead-time axis | domain model § 9 |
| Drought packs, curves and schemes | domain model § 9 |
| A per-country batch for a run over several countries (EX-19, K41, FD-6) | phase-17b-finetune.md |
| A pinned measure shortlist in Adapt for the run form (EX-16, AD-1, K40, FD-5) | phase-17b-finetune.md |
| Coverage side cards beside the Data coverage matrix | SH-10, ui#553 |
| Per-flood-type vs combined aggregation selector for EIOPA (C51) | SH-10, ui#553 |
| E-mail alerts | SH-10, ui#530 |
| Org-wide map-provider configuration served to members | SH-10, ui#532 |
| Assets in alerts on Home (waits on the ADR-078 amendment) | SH-10, ui#538 |
| Streamed assistant replies over SSE | SH-10, ui#565 |
| Assistant-drafted insights on Home | SH-10, ui#539 → ui#565 |
| A second sign-in factor (K57) | #1673 rulings |
| A scheduled conformance job and an engine memory probe (K58) | #1673 rulings |
| Storage and tile history on System health (K59, ui#533) | #1673 rulings |
| Two notification types: report shared, daily assistant summary (K60; amend ADR-037) | #1673 rulings |
| A GADM level 3 result tier and a result band per admin unit (K61); the building tier lands in Phase 18 | #1673 rulings |
| Two Home lens figures: cached cells across runs, municipalities at high risk (K66) | #1673 rulings |
| The reproduction bundle: inputs manifest, parameters, checksum (K71, ui#555) | #1673 rulings |
| Subscription tiers (parked, not a build item) | D20 |
| ui#1249 Engines cards show liveness and method notes (supersedes K74) | found-in:phase-17b; D14 above |
| ui#1177 Distance from a place to a live event's hazard footprint on past-event cards | found-in:phase-17b; D14 above |
| Critical-infrastructure layers for the community view (C108; OSM under the ODbL review, which ADR-089 now gives) | phase-17-revamp.md "Out of scope" |
| Compliance scoring beyond the coverage checklist (C63) | phase-17-revamp.md "Out of scope" |
Phase 17b's repair set: ui#1285 to ui#1296 (CI parity audit), ui#1299, ui#1302 and ui#1307 to ui#1322 (production parity walk, 19 blockers, ui#1284 second comment), and the backbone issues filed for the walk's backbone-caused boxes (#1938 to #1943 as of 2026-10-28 08:20Z), all on the "Phase 17b -- Walk corrections" milestone: not Phase 18 work, landed by /phase-build phase-17b before this phase's first UI view issue (Split readiness; the UI breaker is tripped, 0.41 to 0.42) |
mid-review-phase-17.md; /parity-audit phase-17b; ui#1284 |
Prototype¶
Phase 18 prototype v3, built on the v2 source (climate-lama-ui docs/prototype/v3/source/):
the Lookup building card and Edit home, Explore's building exposure and tier, Portfolios' snap step,
Data's pack card and curve rules, the Reports credit and the Home count, at desktop and phone
widths, with the Direction board (V1–V3, D1–D14, a star on each recommendation; picks stored in the
artifact's picks collection as in Phase 17). Artifact: claude.ai/artifact/3mNidy2NJcGnP1yRdvL1me (private to the
owner; version 1 is the first publish, version 2 carries the pass-2 corrections). Snapshot and checksums: docs/prototype/v3/ in
the UI repo.
Reassess record¶
Pass 1, 2026-10-28, fast — direction and D1–D14 against the codebase (two refuters, one
completeness critic; backbone 97b3a970, UI ab69f14e). Of 18 claims, 7 confirmed, 10 corrected,
1 refuted-as-cited; the critic added 5. Every correction is folded above.
- Confirmed: D1 (DBSM, with the attributes sourced to JRC142133); D14 (ui#1177's footprint is a
live event's, not a building); the Phase 19+ stub is complete against plan.md's stub; guardrail 4's
vendored real extract matches ADR-077 and the seed's own pattern; the provers exist; the
admin_boundariesshape for the shared layer; the export guard's shape. - Corrected: the slice citation (the Phase 17 record's D13 is themes; property level is D18 and
board item 13; the C-ids live in phase-17-revamp.md, not ui-revamp.md); D2 (ADR-089 is the deferred
licence review; no tenant-facing export before counsel; a loss derived from footprint attributes is
an ODbL figure); D4 (
native_resolutionis on hazard datasets in degrees, Lookup'sresolutionis H3); D5 (the worker builds one impact function today:tasks.py:1970,_build_impfset; the multi-curve set, provenance list and cache key); D6 (no ad-hoc point compute: the per-org "homes" dataset); D10 (asset tiles come from bound arrays, not a table); D11 (a building is an exposure through "My assets", not a scope rung; thebuildingrun-exposure kind and ADR-087); D12/B2 (the ingest pipeline is raster-only and org-scoped: a maintenance-lane loader with a vector reader and its own memory gate); B8 (the sidecar holds one curve: the curve list with shared bins); B16 (no backbone CSV template route: the UI's static template); D13 (the registry id shape and the two pre-split harness issues). - Added (critic and refuters): forced RLS and the policy count on
home_editsandimpact_function_bindings, proven undertests/test_db/where the migrations gate runs; the pack's load lane (no org owns a footprint ingest job); default bindings keyed to the built-in curve's identity and seeded per org; the money line anchored on an org-scoped result row with the edits digest in the key; Postman on every new route (scripts/demo.pyis gone); the fixture's ODbL notice; the binding hash in the cache key and the curve list in provenance. - Open, not cleared by the pass: Cyprus geocoding for the building search (Nominatim on prod is Greece-only, showcase Moment 1 step 1): a building is reached by the pin or by clicking a footprint, so the search is a convenience, not a dependency, and the record does not promise it; the DBSM Greece file size against prod disk (cleared at S1: 2.7 GiB against 72 G free).
Pass 2, 2026-10-28, fast — the prototype v3 against this record (one refuter, one
completeness critic over docs/prototype/v3/source/). Of 9 claims, 3 confirmed, 6 corrected; the
critic added 5. Every correction is in the snapshot and the artifact's second version.
- Confirmed: the 15 registry ids continue each view's v2 series in the v2 call shape (lookup-14 → 15, explore-44 → 45, portfolios-15 → 16, data-17 → 18, reports-14 → 15, home-15 → 16, shell-m3 → m4); the Data, Reports and Home changes; the artifact copy mirrors the source.
- Corrected in the prototype: the Lookup drawer (V1 layout B) dropped every cell-size line and
showed a flood figure with no ≈ 90 m line (guardrail 7): the drawer keeps a summary line and the
money figure names its cell in both layouts; a snapped footprint now resolves to its asset in
Explore (
?exposure=asset:<id>) and only a bare footprint isbuilding:<id>with its modelled value as an ODbL figure (D11); the building tier is coloured by expected annual impact where a member is snapped, faint elsewhere (D10, V2), not by the hazard score; the attribute fill from the footprint is an opt-in switch, off by default, on the snap step and the detail page, and the curve cell carries the ODbL tag only where the switch is on and the row had no attributes (D5, B4, U7); a value built from the footprint's area keeps its ODbL tag after an edit that left the value blank (D2); "resolution unknown" renders for a hazard whose dataset records no native resolution (D4); the Data Buildings tab opens from#data.buildingsso its two entries capture without a click; the picker's input filters by id and no longer promises an address search; the snap meter and its toast say "illustrative". - Corrected in this record: D13 gained its options like every other decision; the frame's Admin line (a pack status in Admin) is struck, the pack's status lives on Data's card; U0 says how the click-and-phone states are captured.
- Not verified by the pass (budget): byte identity of
artifact/jswithsource/jsbeyond line counts (this session'sdiff -rqafter the mirror is the proof), and the artifact page's head (this session'sheadread: it starts with the title and loads only the allowed hosts).
Out of scope (deliberate)¶
defer:every row of the Phase 19+ stub above.guard:no tenant value in the footprint layer; no tenant-facing per-building export until counsel ratifies rule 3 (D2); figures derived from footprint attributes carry the ODbL licence.guard:no per-building flood figure without the ≈ 90 m line; no non-flood per-building figure above screening grade (D4, D9).guard:no figure from a legal text without its source; the ENFIA and 5116/2024 lines stay[ASSUMPTION]until the Gazette is read (F-6).guard:CLIMADA and Oasis isolation unchanged; no engine release; the new tenant tables ship with forced RLS.