Files
Northern-Thailand-Ping-Rive…/CLAUDE.md
T
grabowski cf10f2e4c8
CI / Format & lint (push) Successful in 26s
CI / Test suite (push) Successful in 1m15s
Docs / Validate documentation (push) Successful in 12s
feat(dashboard): console phase 4 - 2024 replay, simulation, replaces the old page
The console is now the dashboard at / (src/static/dashboard.html); the old
page and console.html are gone.

- Replay of the October 2024 flood (flood-2024-all.json): the page's clock,
  verdict, list, markers, flood zones and panel follow the replay; the panel
  charts the season on fixed axes with the 2024 model's 24 h peak. Hourly
  while the model alerts or P.1 is above 3.50 m, faster through quiet days;
  pause and drag. Rain, basin stations and the 5-minute refresh are off
  during the replay (today's data).
- ?demo_level= and ?demo_rise= simulate P.1. A violet REPLAY / amber
  SIMULATION bar, the top bar's KPI and the P.1 panel say so; the mode is
  drawn from state.mode alone, so a refresh or language switch cannot
  relabel either as live.
- History: rows without discharge_percent are ThaiWater's copy of the gauge
  (night hours RID publishes late), with a different rating (P.1: 26 against
  RID's 54 m3/s); their discharge is not drawn for gauges with RID discharge.
  The discharge strip has headroom so its label stays clear of the line.
- Swap: the old page's title, description, Open Graph and JSON-LD tags; no
  noindex; tests/test_dashboard.py = console tests + the old checks that
  still apply (one P.1 level, SEO files) + a replay/simulation labelling test.
- Docs: README screenshot (docs/img/dashboard.jpg, replacing two unused
  images), CLAUDE.md, CONTRIBUTING.md, FLOOD_FORECASTING.md (where the model
  shows), DASHBOARD_REDESIGN.md (phase 4 done, mode rules).

Deploy needs a restart: the server reads dashboard.html at start.
2026-10-20 13:47:15 +07:00

4.7 KiB

CLAUDE.md

Guidance for AI coding agents working in this repository.

What this is

Flood monitoring and forecasting for the Ping River, Chiang Mai. Public dashboard and API at https://water.buildfor.life/ (never publish the server's private/mesh IP). Production: one systemd unit, ping-collector.service (the Rust binary in rust/), on a small VPS in /opt/thailand-water-monitor (a git checkout), user water-monitor; plus the monthly water-monitor-retrain.timer (Python, .venv, uv-managed). Updated by git pull, cargo build --release, sudo bash scripts/install.sh.

Rules

  • Python 3.11 only. uv sync --python 3.11; run everything as uv run ....
  • make format (black 88 / isort black profile, config in pyproject.toml; cargo fmt) before committing; CI fails on formatting. make test must stay green. Tests use synthetic data, never the network; database tests (both languages) run against a throwaway PostgreSQL schema from TEST_DATABASE_URL and skip without it.
  • PostgreSQL only. No other database backends.
  • Timestamps everywhere are Asia/Bangkok wall-clock with no offset. The dashboard parses them with parseTs() and renders with timeZone: TZ; keep it that way.
  • Model changes go through the rolling-origin harness (scripts/evaluate_variants.py) and are judged on first-alert LEAD and false alarms, not MAE. Record results, positive or negative, in docs/FLOOD_FORECASTING.md section 5. Do not change what is deployed (rise_rain / hgb-v3) without a harness result that beats it on lead.
  • train_all() must never silently produce a gauge-only (v2) model; the guard that raises RainUnavailableError stays.
  • No git add -A: zero-byte shell-accident files (#, $(wc, ...) have been committed before. Stage files by name.
  • Do not add Co-Authored-By trailers.
  • The dashboard is a single file, src/static/dashboard.html (design and rules in docs/DASHBOARD_REDESIGN.md), EN + TH via the t() table: every user-visible string needs both languages, Thai is the default, no emoji. Every path it fetches must be a route in rust/collector/src/web/mod.rs (tests/test_dashboard.py checks). A replay or a simulation must never be shown as live data. Preview it against production with uv run python scripts/dev_proxy.py; the server reads it at start (restart).
  • Rust (rust/, see rust/README.md): cargo fmt --all, cargo clippy --all-targets -- -D warnings, cargo test; minimum version 1.88 (CARGO_TARGET_DIR=target/msrv cargo +1.88 check --all-targets --locked). Read .env only through rust/collector/src/envfile.rs (python-dotenv semantics, never dotenvy: the Python tools read the same file).
  • The forecast is served by Rust (rust/forecast) from models trained in Python; src/ml/predict.py is the reference it is tested against. A change to src/ml/features.py, predict.py, the HII fill or the dam frame must be ported in the same change: regenerate rust/forecast/testdata/gen_forecast_fixtures.py and keep cargo test -p ping-forecast green. Changing thresholds, stages, leads or the notifier settings (src/ml/export.py): uv run python -m src.ml.export --config (a test fails otherwise) and rebuild the collector.
  • Notifier texts are pinned by rust/collector/testdata/notify_python_cases.json.gz; a deliberate wording change updates that fixture in the same commit.
  • API routes live in rust/collector/src/web. A route's parameters are declared once (web/params.rs); that list validates and documents (/docs). Keep response formats stable: external clients use them.
  • The Python backfill stores (src/hii_collector.py, src/rid_reservoir.py, src/ml/rain.py) write the same tables as rust/collector/src/db.rs: a table or upsert change goes into both.

Where things are

  • rust/collector (ping-collector): collection (rid.rs, hii.rs, dams.rs, rain.rs), forecast run (forecasting.rs), ntfy/Matrix (notify.rs), website/API (web/), tables (db.rs). rust/forecast: inference.
  • src/ml/: features/train/evaluate/predict/export, rain.py (Open-Meteo), dam.py, hii_rain.py. src/hii_backfill.py, src/hii_collector.py, src/rid_reservoir.py: archive backfills (scripts/backfill_*.py).
  • scripts/install.sh: install/update on the server. scripts/retrain.sh + water-monitor-retrain.timer: monthly retrain with staged promote. scripts/forecast_parity.py: Rust vs Python forecast on a live database. scripts/dev_proxy.py: serve the working-copy dashboard against the live API.
  • docs/FLOOD_FORECASTING.md is the authoritative model write-up; docs/DATA_SOURCES.md the source catalog; docs/NOTIFICATIONS.md the alerts; docs/RUST_REWRITE.md the history of the Python-to-Rust move.