Skip to content

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.html line 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 carries value_source="modelled" or attributes_source="footprint" and license="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 and footprint_id drops 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 Eurostat sts_copi lift, 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's native_resolution (degrees per pixel, models/hazard.py lines 223–226) as native_resolution × 111 320 × cos(lat) east–west with the north–south figure or the larger of the two beside it, null with "resolution unknown" when the column is null, and never the lookup's H3 resolution (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.py lines 13–14 and 39) and resolved per org, seeded lazily beside seed_builtin_impact_functions, with org-level overrides; the resolver reads the tenant's own occupancy, storeys, epoch from exposures.properties first 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-point impf_id from the resolution (replacing the constant np.ones at worker/tasks.py line 1970), the adapter builds an ImpactFuncSet of 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/impact with the one-row selection, so the result cache key (dataset id and sha plus the membership hash, core/result_cache_service.py lines 123–125 and 216–241) covers footprint, value and edits with no new route; the job writes an org-scoped impact_results row that the cache row's result_id anchors (orphan purge, worker/tasks.py lines 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 new POST /v1/lookup/building-impact that builds the one-point exposure in core and mints its own key; (c) compute it in core from the curve and the cell intensity; (d) no money line. Recommended: (a): compute_service.py line 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_edits with org_id, FORCE ROW LEVEL SECURITY and the org_isolation policy like exposures (admin_boundaries is 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' assets layer is built from the result's per-asset arrays bound into the SQL (db/repositories/vector_tile_repository.py lines 280–310, from the eai_exp file in MinIO), not from a table, so core/result_asset_tiles.select_points fetches footprint_id per exposure id (an RLS-scoped exposures read) 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 new buildings layer; (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 in tile-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 kind building (src/run/runAddress.ts parse and format, building in scopeKinds.ts SCOPE_KIND_IDS, set_scope accepting exposure=building:<footprint_id> in src/actions/explore/setScope.ts), and the backbone's run contract (ADR-087, B18) accepts a footprint exposure as one point with the value from home_edits else 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-18 run 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/ plus SHA256SUMS), registers its changes under window.CL18 in js/changes18*.js with the same call shape as js/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-point scripts/prototype_registry_coverage.py (its glob changes17b*.js and defaults, lines 46, 75, 179–182) and the UI's render.sh, render.mjs, prototype-src, states/index.json and the README's parity profile at v3. Every UI issue carries its parity box, state file and Prover: from the first issue (mid-review F-1, F-2).
  • D14 — The two open found-in:phase-17b issues. 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 table building_footprints with no org_id and no row-level security, like admin_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_id is a nullable indexed reference (migration, architecture rule 5). home_edits and impact_function_bindings are org-scoped tenant tables with FORCE ROW LEVEL SECURITY and the org_isolation NULLIF policy (the app role climate_lama_app is NOBYPASSRLS, CLAUDE.md "Gotchas"); the policy count at tests/db/test_app_role_rls_isolation.py line 544 rises, both tables join _ORPHANED_ORG_SCOPED_TABLES, and a tests/test_db/ assertion proves it where migrations-gate.yml runs (it runs tests/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 its ingest_jobs and ingest_chunks are org-scoped (docs/DEPLOYMENT.md line 895), while the footprint layer has no org. So scripts/packs/building_footprints.py is a chunked script loader in the shape of scripts/load_admin_boundaries.py, run as the owner or maintenance DSN of the lane table in docs/DEPLOYMENT.md (lines 612–643): a vector reader (pyogrio.read_arrow by feature offset or bbox chunks) staged from MinIO, COPY into the footprint table one chunk in memory at a time, dataset_identity.py for the identity, and a new memory-gate test beside the raster gate (tests/worker/ingest/test_memory_regression_gate.py covers 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 core into the per-point impf_id array; the worker loads every resolved curve and the adapter builds the ImpactFuncSet of all of them (D5), the CLIMADA sidecar takes impf_ per exposure as it does today, and the Oasis sidecar takes a list of curves (docker/oasis-sidecar/model_builder.py holds one curve today at lines 690–740: one intensity bin scheme from the largest x[-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.csv already writes a VulnerabilityID per 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 buildings layer from z 15 by binding footprint_id per 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 from home_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.py loads 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 in tests/climate-lama.postman_collection.json (scripts/demo.py no 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:

  1. 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 its Design: line at split time; the builder reads the captures before editing and the PR's visual captures before merge.
  2. 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.
  3. 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.
  4. Prod-shaped fixtures. The seed holds the vendored real footprints around both demo pins with their notice, an exposure dataset with occupancy, storeys and epoch, a member with no value (modelled), a member with no footprint within the snap distance, and every fixture currency and unit prod holds.
  5. 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 under tests/test_db/), oasis-conformance.yml, the memory gate, performance.yml, or needs-prod-proof at the release. No box without one leaves the split.
  6. 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_id on it, the credit on every map and PDF, and that every result and exposure GeoJSON and every per-building export emits only Point geometry at the tenant's own coordinates with no footprint attribute but footprint_id.
  7. Honest cells and honest ownership. Every per-building figure carries cell_size_m and 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 carries license="ODbL-1.0" and never enters a tenant-private export (D2).
  8. Tenant isolation on the new tables. home_edits and impact_function_bindings ship 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).
  9. Every new route updates the SDK and the Postman collection (B3, B4, B6, B12–B15, B18).
  10. 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_m2 uses.
  • 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 4326 and promotes Polygon to MULTIPOLYGON.
  • Licence. licenseODbL.txt and the report (§ 4.1.1, PDF p. 15, "distributed under ODbL-1.0 license") put the database under ODbL 1.0. copyright.txt adds 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:

  1. The construction cost per m² comes from the power law of Table 3-25 (y = 24.1 · GDPpc^0.385 for residential). The construction-cost surveys are weighted by their share of floor space (§ 3.3.1.2).
  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_data is 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.txt of the R2025 folder)".
  • B2: a box: "storeys = max(1, round(height ÷ 3)), the convention of DBSM's floor_area_m2; epoch stored as DBSM's bin code with 0 as null; a use of 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_copi are 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)

  1. Moment 1, walked on prod: at the Greek pin 40.60, 22.80 and the Cyprus pin 34.89, 33.61 Lookup 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.
  2. Moment 2, walked on prod: a portfolio uploaded with occupancy, storeys and epoch snaps 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.yml and e2e-smoke.yml portfolio-upload.spec.ts.
  3. Moment 3: "Compare engines" on the bound portfolio agrees within the tolerance of verify_engine_agreement.py across climate_lama, climada and oasis, and the two-curve conformance case passes. Prover: sdk-smoke.yml, oasis-conformance.yml.
  4. Demo slice unchanged and green (scripts/smoke_e2e.py).
  5. 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.
  6. 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.
  7. Tenant isolation: the RLS assertions on home_edits and impact_function_bindings green under migrations-gate.yml; the cross-org money-line case green.
  8. Guardrails and the breaker: the whole-phase escape rate reported in mid-review-phase-18.md whatever its value, every trip with its repair set; /parity-audit phase-18 filed.
  9. 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-17b picks them up:
  • The CI /parity-audit phase-17b of 2026-10-28 (E2E Visual run 40593592450 on UI ab69f14): ui#1285 to ui#1295, 21 parity mismatches on eleven views, and ui#1296, the visual stack lacking the seed.spec.ts rows that 30 state captures need.
  • The production parity walk of 2026-10-28 (prod, UI v0.21.0, backbone v0.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 (503 E_SERVICE_BUSY instead of 500 while the database is unavailable). Read 2026-10-28 08:20Z from gh; 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_boundaries shape 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_resolution is on hazard datasets in degrees, Lookup's resolution is 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; the building run-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_edits and impact_function_bindings, proven under tests/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.py is 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 is building:<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.buildings so 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/js with source/js beyond line counts (this session's diff -rq after the mirror is the proof), and the artifact page's head (this session's head read: 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.