v1.1.0 — Demo & polish
port-tidewatch v1.1.0
Release date: 2026-06-12
Demo & polish (M6). v1.0 was a complete, observable pipeline; v1.1 makes it
demonstrable and richer — a scripted storm surge so severe actually happens,
a presentable dashboard, more to watch per gauge, and alert events published off
the stage-change chokepoint.
Demo
The simulator drives one gauge (CUX) through a scripted surge while the others
hold normal, so the full normal → warning → severe → recede cascade is
visible end to end:
If the player does not load, download the clip
(≈ 60 s).
![]() |
![]() |
![]() |
Calm — all normal. |
Peak — CUX severe (5.65 m). |
Held severe (5.75 m). |
What you're seeing. Cards are gauges on a fixed 0–6 m NHN scale; the
shaded bands are the WADI thresholds (warning 4.50 m, severe 5.50 m).
The header summarises gauges per stage, the highest current level, and overall
status, and the live / last-updated indicator makes stale data obvious.
Highlights
- Scripted storm-surge scenario — the simulator now drives a tide baseline
plus a smooth raised-cosine surge (peak/period parameterisable via
SURGE_PEAK_M/SURGE_PERIOD_S), sosevereis reachable and the cascade
tells a story instead of random noise. - Tidal operations dashboard — banded area charts on a fixed 0–6 m domain
(the bands are the thresholds), a header summary, relative time-in-stage
("warning for 3 min"), a live / stale / offline indicator, self-hosted fonts,
and animated stage transitions. - Richer per-gauge signals — the API DTO gains rate-of-change (m/min),
time-in-current-stage, and window min/max, computed in the mapper (state stays
raw, ADR-002) and surfaced as a trend arrow. - Alert-event publishing — a genuine stage change publishes an
AlertEvent
(gauge, previous → new stage, level, timestamp) to a fanout exchange from the
singleApplyStageChangechokepoint (ADR-001), traced consistently with the
existing OpenTelemetry path.
What's changed
Simulator
- Composite level model (baseline + tide + scripted surge + small noise) with
per-gauge roles; one gauge surges, the rest stay normal. TheReading
contract and the publish path are unchanged — only level generation differs.
API & dashboard
GaugeDtogainsrateMetersPerMin,timeInStageSeconds,windowMin,
windowMax; the endpoint passes a singleUtcNowso a response is internally
consistent.- Dashboard reworked: banded charts with a peak marker, header summary, relative
stage time, connection indicator (last good snapshot held across a failed
poll — ADR-002 addendum), dark "tidal operations" theme with self-hosted
Fraunces + IBM Plex Mono.
Ingestion
- New
AlertEventcontract and anIAlertPublisherseam;RabbitMqTransport
declares a fanout alert exchange plus a durable audit queue and owns a
dedicated publish channel. The initialnormalestablishment is suppressed,
so a held stage publishes nothing.
Tests
- Unit coverage for the computed DTO fields; integration tests that a single
transition yields exactly one alert event and a held stage yields none.
Known limitations (by design)
- No notification delivery — the chokepoint publishes alert events, but
acting on them (email / push, a notification consumer) is out of scope. - Simulator is still the sole source — a real PEGELONLINE Elbe feed is the
v1.2 (M7) step. - Dashboard remains read-only and polling-based.
What's next (v1.2)
Real data — see M7 in docs/ISSUES.md: a PEGELONLINE source
adapter (cm → m, PNP → NHN) alongside the simulator, a source-selection switch,
and a threshold "what-if" panel.


