The user chose direction B of the redesign: a full-screen operations console.
It is built next to the current dashboard, served by ServeDir at
/static/console.html (noindex, links back to /), and replaces dashboard.html
once it covers every feature (docs/DASHBOARD_REDESIGN.md, phase 4).
Phase 1:
- top bar: the Chiang Mai verdict (P.1 level + 24 h forecast, the current
dashboard's rule), gauges reporting, highest 24 h flood chance, data age
("overnight pause" instead of "stale" during RID's nightly gap, as in the
notifier), EN/TH, light/dark (dark default), Get alerts
- gauge list: one status rule for list, map and panel (normal, watch within
0.5 m or >= 25 % chance, warning, danger, no data), margin to the warning
level, 72 h sparkline, 6 h change; filter; sort by river order, closest to
the warning level, rising; arrow keys
- map: Ping mainstem and tributaries, status markers with code + level,
layers Rain 24 h (on by default only with heavy rain) and Flood zones
(auto-shown while P.1 is at a stage), one legend
- station panel: level, change, discharge (last RID value when the newest
hour is level-only), warning/danger levels with margins, capacity, level
scale with now and 24 h peak, 72 h chart with the forecast peaks, forecast
table with the model and issue time, the P.1 inundation stages, data links
- alerts drawer with the ntfy topics; selection in the URL (#P.84); refresh
every 5 minutes
- Inter and Noto Sans Thai self-hosted (src/static/fonts, 75 KB, OFL), no
emoji, Thai first, Bangkok time
tests/test_console.py: routed fetch paths, EN/TH parity, translated aria
labels, no emoji, fonts and licences, noindex. dev_proxy.py serves
src/static with real content types (it sent HTML as octet-stream).
4.7 KiB
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 asuv run .... make format(black 88 / isort black profile, config in pyproject.toml; cargo fmt) before committing; CI fails on formatting.make testmust stay green. Tests use synthetic data, never the network; database tests (both languages) run against a throwaway PostgreSQL schema fromTEST_DATABASE_URLand 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 withtimeZone: 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, indocs/FLOOD_FORECASTING.mdsection 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 raisesRainUnavailableErrorstays.- 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, EN + TH via thet()table: every user-visible string needs both languages. Every path it fetches must be a route inrust/collector/src/web/mod.rs(tests/test_dashboard.py checks). Its replacement, the console (src/static/console.html, preview at /static/console.html, plan in docs/DASHBOARD_REDESIGN.md), follows the same rules plus no emoji (tests/test_console.py); preview it withuv run python scripts/dev_proxy.py. - 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.envonly throughrust/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.pyis the reference it is tested against. A change tosrc/ml/features.py,predict.py, the HII fill or the dam frame must be ported in the same change: regeneraterust/forecast/testdata/gen_forecast_fixtures.pyand keepcargo test -p ping-forecastgreen. 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 asrust/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.mdis the authoritative model write-up;docs/DATA_SOURCES.mdthe source catalog;docs/NOTIFICATIONS.mdthe alerts;docs/RUST_REWRITE.mdthe history of the Python-to-Rust move.