Phase 16 — Consistency¶
CLOSED 2026-09-17 — released as backbone v0.26.0 / UI v0.16.0, deployed by run 35244603330, walked on prod by the owner the same day; both milestones at 0 open and all seven exit criteria met. Record: mid-review-phase-16.md — Ready for next phase. State table: the block at the end of this file.
Defined 2026-09-11 by owner instruction (session lama-plan-phase-16), from a
quick owner walkthrough of prod (backbone v0.24.1, UI v0.14.1, GET /v1/info
→ git_sha 117477e, read 2026-09-10 22:01 UTC). A small phase with no epic: it
closes the inconsistencies and minor UX gaps the walk surfaced, mostly in
climate-lama-ui, plus the backbone contracts those fixes need. It runs
beside Phase 15 — Live Risk (open, building) and
opens on the day its split lands; the visual revamp, adaptation measures and the
other Phase 14 deferrals move from "Phase 16" to the Phase 17 charter stub
(D1). Serves Goal C (the showcase UI reads as one product) and, through three
contracts, Goal B.
What it is. Nine owner-reported points (O1–O9 below) and five points found by
the code audit that followed (A1–A5), each grounded against UI trunk e9d0c21
and backbone trunk 319a7b6 and, where it mattered, against prod. Every item
is a fix at the contract or presentation layer of a surface that already
exists; none adds a surface.
What it is not. No redesign (that is the Phase 17 stub in
ui-revamp.md); no new peril, engine or country; nothing on the
/live surface Phase 15 is still building (its patterns land there first). The
/measures route, the dark-mode palette and the address-search coverage of
Cyprus are named out of scope with pointers.
The frame — the same user questions, answered without friction¶
| Surface | Owner finding (walk 2026-09-10) | What Phase 16 changes |
|---|---|---|
| Assess | O1 hazard datasets are one flat list per peril while exposures are grouped by country; O4 a run dies when the user opens another tab; O5 the result map repaints itself into Attica alone a few seconds after rendering; O6 the cell popup has a tiny × and does not close on an outside click | hazard datasets grouped by the server's country (backbone country on GET /v1/hazards); runs owned above the routes; the regional tier only when boundaries cover the result; popups that close like popups |
| Home | O2 five tall cards → endless scrolling; the dashboard should be one look | a denser grid that fits one 1440×900 viewport at prod's data volume, with a measured check |
| Header | O3 a notification marks itself read and nothing happens; O8 no way to tell which page is open | rows navigate to what they name; the active nav entry is highlighted |
| Lookup | O7 address search "unavailable right now"; O9 the hazard raster is fuzz at every zoom and carries no business meaning | geocoding enabled on prod (reverses #1322 on new evidence, D8); the raster rendered in the score scheme's own bands with the legend to match, faded out at street zoom |
Ground truth at open — archived to archive/phase-16-log.md (consumed 2026-09-11).
Design record (D1–D12 — one line each; full prose in archive/phase-16-log.md)¶
One line per decision: the decision, then the one constraint that binds a builder. Nothing is dropped or renumbered — the prose each line compresses is in the archive, and the dated amendments below correct D10 twice.
- D1 — Phase 16 is Consistency; the revamp stub becomes Phase 17; it runs beside Phase 15 — every item on a Phase 15 hotspot carries a fully-qualified
Deps:. - D2 — A hazard dataset's country comes from the server, with a
?country=filter — new row-dict repository methods, filter applied in SQL, never derived client-side. - D3 — Home fits one 1440×900 viewport at prod's data volume — a Playwright assertion behind a volume floor that fails, never skips.
- D4 — Notification rows navigate, read off payload keys the emitter already stamps — no new
NotificationType; the ids ride the wire, not a UI lookup. - D5 — A
RunProviderabove the routes owns the in-flight run ids, plus?job=— only ids move up; result and GeoJSON stay on Assess. - D6 — The regional tier is gated on
admin_coverage, and prod gets the full GADM load — ratio < 0.95 or absent ⇒ point tier; polygons merge, never replace. - D7 — Popups close on outside click, Escape and a reachable × — one CSS rule and one option set, applied to every
maplibregl.Popupinsrc/. - D8 — Geocoding is enabled on prod, reversing #1322 on the owner's re-report — the deploy profile is grep-gated on
GEOCODER_ADVERTISED=trueand inert until it flips. - D9 — The active nav entry is visible via
NavLink+aria-current— no mapping table; exactly onearia-current="page"per route. - D10 — The hazard raster speaks the score scheme's bands, minted server-side —
classed=scoreis refused together withcolormap_nameorrescale. - D11 — One app-wide
ConfirmDialogreplaces the native dialogs — zerowindow.confirm/alert(left outside tests. - D12 —
CostBenefitPaneltakes ui#403's rail parity fix — dropoverflow-y-autoon the aside; the footer isshrink-0.
Guardrails — every Phase 16 issue¶
Phase 14 guardrails 2, 4, 8 and 9 apply verbatim (nothing removed without its
walk; contracts tested, not assumed; dependencies flow downward — the UI never
derives country, coverage or bands client-side; feat:/refactor: for planned
work, fix: only for rework of Phase 16 code, breaker at 25 % via
scripts/rework_rate.py --follow-ups). Plus:
- No item touches a Phase 15 file it does not depend on.
Deps:on the owning Phase 15 issue, fully qualified across repos;/phase-buildresolves a cross-milestone dep by merged PR + trunk grep. - A test that asserts on a MapLibre canvas uses a stable canvas mock (A5); a Phase 16 PR that adds a cursor/popup assertion against the copied mock is red-flagged in review.
- Layout targets are measured. Home's one-viewport rule is a Playwright
assertion at 1440×900 on the smoke stack, and its prove-it-fails is setting
RECENT_RUNS_LIMITback to 10. - The regional tier never hides assets the loaded boundaries do not cover (D6b), on every deployment, including one with zero boundaries.
- Copy names the truth: "assets in
", "indicator", "Assessment running", "Regional view covers N %" — never "cell" for a polygon, never "0 computable" for a peril that computes nothing by design. - Every deploy-workflow edit is grep-gated and inert until the operator
flips the
.env.prodkey (D8), and the PR body records the resolved value of every container- or workflow-consumed key.
Scope — waves and items¶
Dependency line: W0 (backbone contracts) → W1 (UI, independent S items) ∥ W2
(UI, contract consumers) → W3 (ops, owner) → W4 (closeout). W1 has no backbone
dependency and starts on day one. Sizes are drafts; /phase-split grounds and
re-sizes each item with /analyze.
W0 — Backbone contracts¶
| Id | Title | D | Files (seam / hotspot) | Outcome |
|---|---|---|---|---|
| W0.1 (#1369) | feat(api): country on hazard rows + ?country= filter (row-dict repository methods, SQL-level filter) |
D2 | api/v1/hazards.py, db/repositories/hazard_repository.py, tests/test_api/test_hazards_country.py, docs/CLIMATE_LAMA.md (hotspot) |
✅ merged (P16-B1) |
| W0.2 (#1370) | feat(api): admin_coverage on admin-level result GeoJSON + the Attica-popup diagnosis |
D6b/c | api/v1/results.py, core/admin_aggregation.py, tests/test_api/test_results_admin_coverage.py, docs/CLIMATE_LAMA.md (hotspot); diagnosis box routed to W3.1 (Refs) when prod is out of the builder's reach |
✅ merged (P16-B1); diagnosis numbers posted on #1376 |
| W0.3 (#1371) | feat(worker): live_event_id + portfolio_id on the event-lane job_complete; explicit result_id / batch_id on the report_render download_ready |
D4 | worker/tasks.py (hotspot — after #1293), tests/worker/test_compute_event_impact.py, tests/worker/test_report_render_notification.py |
✅ merged (P16-B2) |
| W0.4 (#1372) | feat(api): ?classed=score tile rendering from the score scheme + bands on RasterLegend |
D10 | api/v1/tiles.py, core/tile_render.py, core/scoring.py, models/layer_spec.py, api/v1/hazards.py (layers route), tests/test_api/test_tiles_classed.py, docs/CLIMATE_LAMA.md (hotspot) |
✅ merged (P16-B2) |
| W0.5 (#1373) | feat(scripts): load_admin_boundaries --geojson accepts a GADM FeatureCollection |
D6a | scripts/load_admin_boundaries.py (seam), tests/scripts/test_load_admin_boundaries.py |
✅ merged (P16-B1) |
| W0.6 (#1374) | chore(ci): --profile geocoding in deploy-prod.yml, grep-gated on GEOCODER_ADVERTISED=true |
D8i | .github/workflows/deploy-prod.yml (seam), docs/DEPLOYMENT.md, .env.example.prod (seam, comment only) |
✅ merged (P16-B1) |
| W0.7 (#1375) | feat(api): indicator flag on the catalog coverage cell |
D3 | api/v1/catalog.py (or wherever GET /v1/catalog/coverage lives), core/catalog_coverage.py, tests/test_api/test_catalog_coverage.py, docs/CLIMATE_LAMA.md (hotspot) |
✅ merged (P16-B1) |
| W0.8 (#1406) | feat(api): ?search= on GET /v1/hazards (filed 2026-09-14, after P16-B2) |
— | api/v1/hazards.py, db/repositories/hazard_repository.py, docs/CLIMATE_LAMA.md (hotspot with W0.1) |
✅ merged (P16-B2′) |
| W0.9 (#1407) | feat(api): portfolio_id on JobListItem (filed 2026-09-14, separate from W0.3) |
— | api/v1/jobs.py |
✅ merged (P16-B2′, PR #1436) |
W1 — UI, independent (no backbone dependency; after ui#378/#379 where App.tsx is touched)¶
| Id | Title | D | Files | Outcome |
|---|---|---|---|---|
| W1.1 (ui#427) | feat(ui): active nav entry — NavLink + aria-current |
D9 | src/App.tsx (hotspot — after ui#379), src/test/App.test.tsx (hotspot — ui#379 owns it too) |
✅ merged (P16-U2) |
| W1.2 (ui#428) | feat(ui): popups close on outside click, Escape and a reachable × |
D7 | src/index.css, src/components/ResultMap.tsx, src/components/live/LiveEventMap.tsx (option only), src/test/ResultMap.test.tsx, src/test/mapMock.ts (shared stable canvas mock, A5) |
✅ merged (P16-U1) |
| W1.3 (ui#429) | refactor(ui): one ConfirmDialog replaces native confirm/alert and two hand-rolled dialogs |
D11 | src/components/ConfirmDialog.tsx, src/admin/ConfirmDialog.tsx (moved), src/components/ConfigPanel.tsx (hotspot), src/components/ImpactFunctionEditor.tsx, src/auth/ApiKeysPage.tsx, src/pages/RunsPage.tsx, tests |
✅ merged (P16-U1) |
| W1.5 (ui#455) | feat(ui): Intl.NumberFormat cache in formatNumber (filed 2026-09-14, independent) |
— | src/lib/format.ts |
✅ merged (P16-U1) |
| W1.4 (ui#430) | fix(ui): CostBenefitPanel rail keeps its Run button (aside overflow, min-h-0, shrink-0 footer) |
D12 | src/components/CostBenefitPanel.tsx, src/test/CostBenefitPanel.test.tsx |
✅ merged (P16-U1) |
W2 — UI, contract consumers¶
| Id | Title | D | Deps | Files | Outcome |
|---|---|---|---|---|---|
| W2.1 (ui#431) | feat(ui): hazard datasets grouped by country in the Assess rail; peril labels through hazardLabel |
D2 | W0.1 | src/components/ConfigPanel.tsx (hotspot), src/components/HazardUploadModal.tsx, src/types/index.ts (hotspot — after ui#378), src/test/ConfigPanel.test.tsx, src/test/hazardLabelNamespaces.test.ts |
✅ merged (P16-U2) |
| W2.2 (ui#432) | feat(ui): Home fits one viewport — 3-column grid, 5 runs, error message, indicator chips, volume-floored check |
D3 | W0.7, ui#380 | src/pages/HomePage.tsx (hotspot), src/components/home/*.tsx, src/types/index.ts (hotspot), src/i18n/locales/en/home.json (hotspot — ui#380), tests/e2e/smoke/home.spec.ts, src/test/HomePage.test.tsx (hotspot — ui#380) |
✅ merged (P16-U2) |
| W2.3 (ui#433) | feat(ui): notification rows navigate; mark all read in the bell |
D4 | W0.3, ui#379 | src/lib/notificationText.ts, src/components/NotificationBell.tsx, src/components/home/NotificationsCard.tsx, src/hooks/useNotifications.ts, src/api/client.ts (hotspot — after ui#378), tests |
✅ merged (P16-U3) |
| W2.4 (ui#434) | feat(ui): RunProvider — runs survive navigation; header run pill; ?job= re-attach |
D5 | ui#379 | src/run/RunProvider.tsx, src/App.tsx (hotspot), src/components/Dashboard.tsx (hotspot), src/components/assess/RunStatusPanel.tsx, src/test/RunProvider.test.tsx, tests/e2e/smoke/compute.spec.ts |
✅ merged (P16-U3) |
| W2.5 (ui#435) | feat(ui): regional tier gated on coverage; polygons merge, never replace; admin-unit popup copy |
D6b | W0.2, W1.2 | src/components/ResultMap.tsx (hotspot), src/components/Dashboard.tsx (hotspot), src/components/LayerPanel.tsx, src/i18n/locales/en/results.json, src/test/ResultMap.test.tsx |
✅ merged (P16-U4) |
| W2.6 (ui#436) | feat(ui): banded hazard legend + zoom fade on Lookup and Datasets |
D10 | W0.4 | src/components/lookup/LookupMap.tsx, src/components/datasets/DatasetMap.tsx, src/map/legend.ts, src/api/client.ts (hotspot), src/i18n/locales/en/lookup.json, tests |
✅ merged (P16-U4) |
| W2.7 (ui#437) | feat(ui): country filter on the Datasets hazard and exposure catalogues |
D2 | W0.1 | src/components/datasets/HazardCatalog.tsx, src/components/datasets/ExposureCatalog.tsx, src/api/client.ts (hotspot), src/i18n/locales/en/datasets.json, src/test/HazardCatalog.test.tsx, src/test/ExposureCatalog.test.tsx |
✅ merged (P16-U3) |
| W2.8 (ui#454) | feat(ui): listHazardCatalog passes search + limit (filed 2026-09-14, cross-repo precondition W0.8) |
— | W0.8 | src/api/client.ts (hotspot), tests/e2e/smoke/contract-map.ts |
✅ merged (P16-U3) |
W3 — Operations (trackers; routed by Blocker:, not by label)¶
| Id | What | Blocker |
|---|---|---|
| W3.1 (#1376) — ✅ closed | Load GADM 4.1 GRC levels 1–2 and CYP levels 1–2 on prod through W0.5; record SELECT iso3, admin_level, count(*) before/after per level; run W0.2's diagnosis against the owner's Greece run (count_by_centroid.sum(), n_centroids, max(array_index), the job's portfolio scope) and post the numbers on W0.2's issue; re-open that run on /assess and zoom out |
Blocker: agent — executed 2026-09-16, closed; the diagnosis numbers are posted on the issue |
| W3.2 (#1377) — ✅ closed | Enable geocoding on prod per D8ii; record import wall time, disk and RAM; verify /v1/geocode 200 and features.geocoding = true |
Blocker: agent — executed 2026-09-16, closed 2026-09-17; the Moment 1 step 1 address-search walk ran on prod (owner, 2026-09-17) — Cyprus addresses stay unresolved (Greece-only PBF); the deploy-log confirmation box moved to the phase-end walk (#1379) |
W4 — Closeout¶
| Id | Title | Files | Outcome |
|---|---|---|---|
| W4.1 (#1378) | docs: Phase 16 closeout — plan banner, showcase markers, ui-revamp stub → Phase 17 |
docs/plan/plan.md (hotspot), docs/plan/showcase-script.md (hotspot), docs/plan/ui-revamp.md, docs/plan/phase-16-consistency.md |
✅ this PR — plan banner + status log, showcase Moment 1 step 1, phase doc annotations; no CLAUDE.md edit (no address-search sentence on trunk to update) |
| W4.2 (#1379) | Release (one coordinated bump, both repos), then the one phase-end walk (exit criterion 2) and mid-review-phase-16.md |
owner + orchestrator; docs/plan/mid-review-phase-16.md |
✅ done 2026-09-17 — v0.26.0 / UI v0.16.0 deployed (35244603330), owner walk of all nine points verified, mid-review-phase-16.md written |
Exit criteria¶
- Build issues at 0 open on the
Phase 16 -- Consistencymilestone in both repos. Trackers do not count — aBlocker: credential:*tracker leaves the milestone for the standingopslist rather than holding the phase open; a decision is settled in-run by the framework's decision protocol (2026-09-17) and is never a tracker. - One phase-end walk, by the owner on prod, run once after the release,
scripted from the showcase moments this phase touched: Moment 1 (an
address resolves on
/lookup; the hazard raster shows the card's bands and fades at street zoom) and Moment 2 (Assess: hazard datasets grouped by country; a run started on/assessis still running, or finished with a toast, after a visit to/lookup; the Greece result keeps every asset when zoomed out and shows regions only where the boundaries cover them; the cell popup closes on an outside click) — plus the Home and header points every moment enters through (Home in one 1440×900 view, a notification that opens what it names, the open page highlighted). Findings are tagged and either fixed in one closing wave or filedfound-in:phase-16and drained by the next/phase-split. - Measured: the Home viewport assertion and the coverage-sum test are green on trunk and each has a recorded prove-it-fails run.
- Contracts:
GET /v1/hazardsrows carrycountry; result GeoJSON at an admin level carriesadmin_coverage; the event-lanejob_completepayload carrieslive_event_id, thereport_renderdownload_readypayload an explicitresult_idorbatch_id; coverage cells carryindicator;deploy-prod.ymlcarries the grep-gated geocoding profile;tests/e2e/smoke/contract-map.tscovers every new client method. - Prod data:
admin_boundariesholds every GADM 4.1 level-1 and level-2 unit ofGRCandCYP;GET /v1/info→features.geocoding = true. - Guardrails hold (1–6 above) and the escape rate is ≤ 25 % (fix PRs + behaviour-changing follow-ups + unattributed fix PRs, per build).
- Record:
docs/plan/mid-review-phase-16.mdwith a verdict, tagged findings and the two rework counters (escape rate,partial rate) fromscripts/rework_rate.py --follow-ups;plan.mdbanner and status log flipped; the Phase 17 stub named.
Conflict hotspots (files touched by >1 item, or by a Phase 15 item)¶
UI: src/App.tsx + src/test/App.test.tsx (W1.1, W2.4 — serial; both after ui#379, which owns both files), src/components/Dashboard.tsx
(W2.4, W2.5 — serial), src/components/ResultMap.tsx (W1.2 then W2.5), src/components/ConfigPanel.tsx
(W1.3, W2.1 — serial), src/api/client.ts + src/types/index.ts (W2.1, W2.2, W2.3, W2.6, W2.7 — after ui#378, whose body says no later UI issue edits
them: the split annotates ui#378 with this phase's consumers; one consumer at a time),
src/pages/HomePage.tsx + home.json + HomePage.test.tsx (W2.2 after ui#380), src/index.css (W1.2 only),
src/components/datasets/*Catalog.tsx (W2.7 only),
tests/e2e/smoke/contract-map.ts (every W2 item that adds a client method — serial), src/i18n/locales/en/*.json
(per-surface files, one owner each). Backbone: docs/CLIMATE_LAMA.md (W0.1, W0.2, W0.4 — one docs
region each), worker/tasks.py (W0.3 after #1293), api/v1/tiles.py + core/tile_render.py (W0.4 only),
.github/workflows/deploy-prod.yml (W0.6 only; Phase 15 W0.3 #1281 already landed there).
Orchestration sketch (with its 2026-09-11 wave plan) — archived to archive/phase-16-log.md (consumed 2026-09-15).
Orchestration — the closed record¶
Every wave plan this phase ran under is archived; the state block below is the record a reader
needs, and /phase-analyze reads it.
Amendment 2026-09-14 — folded into the 2026-09-15 amendment and the closed wave plan; archived to archive/phase-16-log.md.
Amendment 2026-09-15 — the phase's last wave plan; every row closed 2026-09-17, so its state lives in the block below and the section is archived to archive/phase-16-log.md.
Phase state — 2026-09-17 · CLOSED (by /phase-analyze, origin/main 5fb5603)¶
State — P16 · Consistency · read 2026-09-17 · origin/main 5fb5603 · UI origin/main 1c713e9
| Wave | Status | Owner | Issues | Deps | Command | Budget | Notes |
|---|---|---|---|---|---|---|---|
| P16-B1 | ✅ done — gate 34786761479 green at e8f1be9 |
agent | ✓1369 ✓1370 ✓1373 ✓1374 ✓1375 | — | /phase-build 1369 1370 1373 1374 1375 --no-pr-ci --max-runtime 270 |
270/102 | lama-phase-build-P16-B1 · $277 |
| P16-B2 | ✅ done — gate 34965255088 green at 7ad780c |
agent | ✓1371 ✓1372 | — | /phase-build 1371 1372 --no-pr-ci --max-runtime 120 |
120/~30 | lama-phase-build-P16-B2 · own closeout ended blocked on the merge classifier |
| P16-W | ✅ done — walk green at 5a280e1 |
agent (WSL) | — | — | scripts/smoke_walk_wsl.sh --require-merged 1401 1403 1404 |
—/~20 | lama-phase-build-P16-W · ghcr titiler proven |
| P16-B2′ | ✅ done — gate 35198886403 green at 1efb351 |
agent | ✓1406 ✓1407 | #1372 ✓ | /phase-build 1406 1407 --no-pr-ci --max-runtime 120 |
120/— | lama-phase-build-P16-B2' · SDK parity fixed in-wave (PR #1435) |
| P16-U1 | ✅ done — e2e-smoke 35104975963 green at 5d31ff1 |
agent (UI) | ✓ui428 ✓ui429 ✓ui430 ✓ui455 | — | cd ../climate-lama-ui && /phase-build 428 429 430 455 --no-pr-ci --max-runtime 150 |
150/— | built in lama-ui-phase-build-P15-U1-U4 (stale name) |
| P16-U2 | ✅ done — e2e-smoke 35104975963 green at 5d31ff1 |
agent (UI) | ✓ui427 ✓ui431 ✓ui432 | — | cd ../climate-lama-ui && /phase-build 427 431 432 --no-pr-ci --max-runtime 200 |
200/— | same session · residue ui#471 filed |
| P16-U3 | ✅ done — e2e-smoke 35208961985 then 35227516009 green at 1318152 |
agent (UI) | ✓ui433 ✓ui434 ✓ui437 ✓ui454 | #1371 ✓ #1406 ✓ #1439 ✓ | cd ../climate-lama-ui && /phase-build 433 434 437 454 --no-pr-ci --max-runtime 240 |
240/— + 120/~75 | lama-phase-build-P16-U3 · ui#437's exposure half took backbone #1439 mid-wave |
| P16-U4 | ✅ done — e2e-smoke 35215947303 green at c388fc7 |
agent (UI) | ✓ui435 ✓ui436 | ui#428 ✓ ui#434 ✓ | cd ../climate-lama-ui && /phase-build 435 436 --no-pr-ci --max-runtime 200 |
200/— | PRs #476 #477 |
| P16-O1 | ✅ done — #1376 closed by agent proof | owner | ✓1376 | v0.25.0 on prod ✓ | owner-run | — | GADM 4.1 on prod: GRC 1:8 2:14 · CYP 1:5 |
| P16-O2 | ✅ done — #1377 closed, walk verified | owner | ✓1377 | v0.25.0 on prod ✓ | owner-run | — | nominatim healthy at ~26 min · features.geocoding: true |
| P16-B3 | ✅ done — gate 35242713348 green at 3eaaf04 |
agent | ✓1378 | every build issue merged ✓ | /phase-build 1378 --no-pr-ci |
—/~25 | lama-phase-build-P16-B3 · PR #1442 |
| P16-R | ✅ done — v0.26.0 / UI v0.16.0 on prod, walk green | owner + orchestrator | ✓1379 | B3 ✓ | /release + owner walk + mid-review-phase-16.md |
— | deploy 35244603330 · all nine points verified 2026-09-17 |
Next — P16
| # | Wave | Command | Session | After |
|---|---|---|---|---|
| — | none | the phase is closed; its findings live in mid-review-phase-16.md | — | — |
- closed — both
Phase 16 -- Consistencymilestones at 0 open; 15 backbone issues, 13 UI issues - release — climate-lama
v0.26.0at3eaaf04· climate-lama-uiv0.16.0at1c713e9· deploy-prod 35244603330 success - gates — backbone
ci.yml35242713348 green at3eaaf04; UIci.yml35242720688 +e2e-smoke.yml35242724086 green at1c713e9 - breaker — escape rate 0.20 backbone (5/25) · 0.19 UI (3/16); partial rate 0.24 · 0.81 — both escapes under the 25 % gate
- carried — ui#471 (5-watch Home proof, needs a Docker stack, no milestone) · Cyprus level-2 boundaries absent on prod · UI issue sizing · split-time route premise check
- drift — the three UI waves ran in a session named for Phase 15, so their per-wave cost and runtime are not attributable
Decisions taken autonomously — archived to archive/phase-16-log.md (consumed 2026-09-11).
Reassess record (2026-09-11) — archived to archive/phase-16-log.md (consumed 2026-09-11).
Out of scope (deliberate)¶
guard:the visual redesign of any surface → Phase 17 stub in ui-revamp.md.guard:the/livesurface and everycomponents/live/*behaviour → Phase 15 (only W1.2's shared popup option/CSS touchesLiveEventMap).defer:Cyprus address coverage (a merged GRC+CYP PBF or a second nominatim instance) → recorded here; revisit when a Cyprus walk needs an address.defer:a download link for a matrix report from itsdownload_readyrow → the row links to/assess?batch=and the strip's report button downloads it; a direct object download needs a matrix-report route.defer:a?job=deep link from ajob_failednotification → needs a Runs hub that lists jobs (ui#377 item 2's backbone counterpart names the gap); the row expands inline instead.defer:a?hazard=<id>/?exposure=<id>selection on/datasetsso adataset_availablenotification can open its row → recorded here; W2.3 links to/datasetsuntil then.defer:search/limitonGET /v1/hazards→ ui#421 item 6 (needs a backbone issue when the catalogue grows past one transfer).defer:level-2 choropleth between z8 and z10 → after D6a proves level-1 coverage on prod.guard:dark-mode palette (RAG colours inert in dark mode) → Phase 17.guard:the/measuresroute's deletion → Phase 14 D9 owner.guard:no newNotificationType, no push, no e-mail (ADR-037, Phase 15 guardrail 9).
Inputs from open residue and their disposition — archived to archive/phase-16-log.md (consumed 2026-09-11).
Split readiness (with Contracts pinned and Split cards) — archived to archive/phase-16-log.md (consumed 2026-09-11).