Phase 4 of docs/RUST_REWRITE.md. Since 2026-10-18 ping-collector (rust/) collects, forecasts, notifies (ntfy + Matrix) and serves the website/API; nothing in production ran the Python service code any more. Removed (~17k lines): the FastAPI app (web_api, schemas, health_check, metrics, postgres_history, forecast_history, thaiwater), the Python RID collector and CLI (water_scraper_v3, main, run.py), the notifier and Matrix alerting (notify, alerting), the multi-backend adapters (SQLite, MySQL, InfluxDB, VictoriaMetrics) with demo databases, models/validators/exceptions/ logging/rate limiter, ml/skill.py (served by Rust now), the migration tooling (api_parity, collector_parity, e2e_notify, install_collector.sh, gen_notify_cases.py, spikes/rust-forecast), Docker/compose/Grafana files, sql/, the release workflow and the docs for all of that. Their tests went with them. Kept in Python: src/ml (training, evaluation harness, export; predict.py as the reference the Rust forecaster is tested against), the HII and RID-dam clients/stores for the archive backfills (their live collectors removed), the backfill scripts, retrain.sh + timer, forecast_parity.py. - PostgreSQL only: src/config.py is database_url() from POSTGRES_* / .env; the stores lost their sqlite/mysql branches (and the 2026-08 column migration). The database tests run against a throwaway schema from TEST_DATABASE_URL (tests/conftest.py pg_url, skipped without it), as the Rust tests do; CI gets a PostgreSQL service. 100 tests (66 without a DB). - The notifier settings moved from src/notify.py into src/ml/export.py; flood_config.json regenerates byte-identical. - tests/test_dashboard.py checks every path the dashboard fetches against ping-collector's router. - Dependencies: fastapi, uvicorn, pydantic, influxdb, pymysql, psutil, schedule, sphinx, pyinstaller, mypy, pytest-asyncio dropped; python-dotenv 1.1.1 explicit (it came in through uvicorn[standard]). - Formatting contract extended to scripts/ and tests/ (reformatted once). - scripts/install.sh is the one installer (fresh host or update, in place in a checkout without touching its ownership): binary, units, uv sync, .env defaults, removes the retired water-monitor.service and any old --alert-check cron job, dry run + forecast check, restart, readiness wait. - Docs, CLAUDE.md, CONTRIBUTING.md, .env.example, Makefile, unit comments and Rust doc comments updated to the Rust service.
4.4 KiB
4.4 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). - 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.