# CRT upgrade package — 2026-08

Everything in this folder is **new and additive**. Nothing in your live
`crt/` or `climate_analysis_tool/` files has been modified. Read
`AUDIT_AND_UPGRADE_PLAN.md` first for the full reasoning; this file is the
apply/rollback mechanics.

## Correction (applied 2026-08-30)

`index_v2.html` and `landing.html` use relative script/asset paths
(`./app.js`, `./drip.js`, etc.) that only resolve correctly sitting at the
`crt/` webroot, next to your production `index.html` — **not** inside this
`upgrade_2026-08/` subfolder. Both files belong at:
```
crt/index_v2.html
crt/landing.html
```
An earlier copy placed inside this subfolder was wrong and has been moved to
`crt/_to_delete/` — safe to delete once you confirm the root copies work.

## Design revision (applied 2026-08-30): population moved to Vulnerability

Per explicit direction, population/settlement is now a **Vulnerability**
factor (`public.settlement`, scored by `settlement_vulnerability.php`/`.js`),
not an Exposure factor. `drought` (`public.drought`) is the real Exposure
addition instead. This superseded the original design in
`AUDIT_AND_UPGRADE_PLAN.md` §5.1 (that document has been updated in place —
see its "REVISION" note at the top of §5.1). Practical effect on this
package:
- `php/population_exposure.php` and `js/population_exposure.js` are
  **superseded** — moved to `_to_delete/`. Use
  `php/settlement_vulnerability.php` / `js/settlement_vulnerability.js`
  instead.
- `js/hazard_layer_profiles.js`'s weight matrices were rebalanced: every
  `EXPOSURE_WEIGHTS` row had `population` removed and its weight
  redistributed across the row's physical layers; every
  `VULNERABILITY_WEIGHTS` row gained a `settlement` component.
- The soil table (`public.soil`) is now populated — the same
  `-lco PRECISION=NO` fix used for `Settlement.shp` worked here too.
  `php/soil_vulnerability.php` has been rewritten to read `gst_code`/
  `gst_label`/`risk`/`score` directly (same pattern as `drought_exposure.php`).

## Design addition (applied 2026-08-30): road-corridor buffer for settlement vulnerability

Per explicit user design, when Infrastructure = Road and a road is
selected, the settlement/population vulnerability score now comes from
`php/road_settlement_vulnerability.php` instead of the point-based
endpoint: it buffers the SELECTED ROAD'S FULL LINE GEOMETRY (default
500 m, adjustable to 250/1000 m via the new dropdown on the Settlement
card), intersects with `public.settlement`, and sums population —
full `set_pop` for settlements entirely inside the buffer, area-weighted
for partial overlaps. `js/settlement_vulnerability.js` picks this mode
automatically (road selected → buffer mode; otherwise → the original
point/nearest mode) and both modes publish to the same
`window.settlementVulnerabilityScore` global, so nothing else needed to
change. See `AUDIT_AND_UPGRADE_PLAN.md` §5.2 for the classification bands
and a flagged caveat about road-length sensitivity in the raw-count score.

## 1. Where things go

This folder (`crt/upgrade_2026-08/`) already sits inside your `crt` webroot,
so `php/*.php` and `js/*.js` are reachable at
`/crt/upgrade_2026-08/php/...` and `/crt/upgrade_2026-08/js/...` once you
copy/commit it into place — no Apache config changes needed.

The `python/` file belongs next to your other Python scripts:
```
climate_analysis_tool/upgrade_2026-08/python/bmd_yearly_quantile_model.py
```

## 2. Apply order (Phase 1 — safe, additive, testable in isolation)

1. **Database**: run `sql/001_new_layers.sql` against the `crt` database —
   at this point it only adds indexes, since `drought` and `settlement` are
   already imported and populated on your VPS. `soil` needs re-importing
   first (see §3 of that file for the diagnosis and corrected `ogr2ogr`
   command); until then, `soil_vulnerability.php` degrades gracefully
   (`found: false`).
2. **Copy this whole `upgrade_2026-08/` folder** (minus `_to_delete/`) into
   your `crt/` webroot, `index_v2.html` and `landing.html` into `crt/`
   itself (see "Correction" note above), and the `python/` subfolder's file
   into `climate_analysis_tool/upgrade_2026-08/python/`.
3. **Test the new endpoints directly** before touching any HTML, using a
   point you know the answer for:
   ```
   /crt/upgrade_2026-08/php/drought_exposure.php?lon=88.9&lat=26.3
   /crt/upgrade_2026-08/php/settlement_vulnerability.php?lon=88.9&lat=26.3
   /crt/upgrade_2026-08/php/soil_vulnerability.php?lon=88.9&lat=26.3&hazard=Flood
   /crt/upgrade_2026-08/php/road_settlement_vulnerability.php?roadcode=<a real ROADCODE>&radius_m=500
   ```
4. **Open `crt/index_v2.html`** (side by side with your production
   `index.html` — it is a separate file, nothing is overwritten) and click
   through a real Upazila/Road selection. Confirm the Drought card (Exposure
   pane) and the Soil/Settlement cards (Vulnerability pane) populate, and
   that the existing cards behave exactly as in production (they are the
   same markup, same IDs, same scripts).

## 3. Integration hooks needed for the hazard-conditional formulas (Phase 2)

`exposure_v2.js` works with **zero changes** to existing files (it reads the
rendered exposure table via the same technique `road_profile.js` already
uses for `getRasterValueByLabel`). `vulnerability_v2.js` needs **one small,
additive hook** in `road_profile.js`'s `renderVC()` function, because the
individual V-component scores (surface/condition/drainage/embankment) are
currently local variables, not published globals.

Add these four lines inside `renderVC()`, right after
`window.finalCapacityScore = C;` (do not change anything else in that
function):

```js
window.roadVulnerabilityComponents = {
  surface: s.worst_surface_vulnerability_score,
  condition: getConditionScore(),
  drainageWeakness: getYesNoNaVulnerabilityScore("drainageWeakness"),
  embankment: embV
};
window.dispatchEvent(new CustomEvent("crt:roadVulnerabilityComponentsUpdated", { detail: window.roadVulnerabilityComponents }));
```

That is the only edit to an existing production file anywhere in this
package. Everything else is new files only.

## 4. BMD routing change (Phase 4 — optional, do after Phase 1–2 are validated)

In `routes/bmdyearly.js`, the branch that currently does:
```js
const payload = { geocode: Number(geocode), year: Number(year), target: target || "yearly_max_rain" };
const py = spawn("/var/www/climate_analysis_tool/venv/bin/python",
  ["/var/www/climate_analysis_tool/bmd_yearly_model.py", JSON.stringify(payload)]);
```
should call `bmd_yearly_quantile_model.py` instead. That script returns
`{ok:false, error:"year_beyond_baseline", ...}` for any year past the
observed record instead of extrapolating — handle that response by calling
your existing `/crt/php/ssp_climate.php` path for the future-year signal
(the front end's `climate_proj.js` already computes `bmdYear` capped at a
safe horizon; extend that same cap logic to redirect to the SSP endpoint
rather than calling the yearly model past `train_year_max`). See
`AUDIT_AND_UPGRADE_PLAN.md` §6 for the full rationale.

## 5. Security hardening notes (Phase 5 — do independently, any time)

- `bmd_yearly_quantile_model.py`'s `DB_URL` is written as
  `postgresql://postgres:CHANGE_ME@localhost:5432/crt` deliberately, to flag
  that the existing `Zazabar65_` password is hard-coded in five files today
  (`php/config.php`, `bmd_lightgbm.py`, `bmd_yearly_model.py`,
  `raster_exposure.py`, `ssp_climate.py`) and in your Git history if this
  folder is version-controlled. Move it to an environment variable
  (`os.environ["CRT_DB_URL"]` / PHP `getenv()`) and rotate the password once
  it is out of the repo.
- None of the new PHP endpoints in this package add authentication — they
  match the existing endpoints' (lack of) auth for consistency during
  testing. Put all of `php/*.php` (existing and new) behind the same
  API-key/session check when you do the Phase 5 hardening pass.

## 6. Event ordering note (relevant if you extend `index_v2.html` further)

`final_risk.js` registers its `crt:*` listeners inside a
`document.addEventListener("DOMContentLoaded", ...)` callback. Any new
script that needs to read a value *after* `final_risk.js` has recalculated
it for the same event must also defer its own listener registration inside
a `DOMContentLoaded` callback, in a `<script>` tag placed after
`final_risk.js`'s — DOMContentLoaded callbacks run in registration order, so
this guarantees `recalcRisk()` runs before your handler on every shared
event. `index_v2.html`'s sidebar script does this already; keep the same
pattern for anything else you add.

## 7. Test checklist before merging into production `index.html`

- [ ] Drought score returned matches the known class for a handful of
      spot-checked upazilas (Tentulia = 57790 should show a mix of
      Moderate/Severe depending on exact point, per the sample dump in the
      audit report).
- [ ] Settlement, soil, and drought endpoints all return real values (all
      three source tables are populated).
- [ ] Selecting a road (Infrastructure = Road) shows the corridor-buffer
      breakdown table on the Settlement card, with a sane total population,
      and changing the buffer-radius dropdown re-fetches with the new
      radius. Switching Infrastructure away from Road falls back to the
      point/nearest method without a stale road-mode number lingering.
- [ ] Selecting different hazards in the Risk Engine visibly changes the
      "computed for: <hazard>" tag on the Exposure and Vulnerability cards,
      and changes the Combined Exposure / Vulnerability scores accordingly.
- [ ] Final Risk Engine numbers for a known existing case (recorded from
      current production `index.html` before applying Phase 2) are compared
      side-by-side against the new hazard-conditional numbers, and the
      difference is explainable by the weight matrix in
      `AUDIT_AND_UPGRADE_PLAN.md` §5.1 — not a bug.
- [ ] Report generation (`report_generate_v3.js`) still works unchanged
      (it was not touched).
