port-tidewatch v1.3.0
Release date: 2026-06-16
Readability (M9). v1.2 brought real data; v1.3 makes the displayed values
legible — what each number means and how current it is. This milestone
replaces the dropped M8 observability work (see the ADR-003 amendment): the
OpenTelemetry path stays wired but unsurfaced, and the effort went into honest,
domain-true freshness instead.
Highlights
- Freshness reflects the measurement, not the poll — the per-gauge indicator
showed the age of the last successful API poll, so a fresh poll against a
minutes-old PEGELONLINE reading read as "live". It now shows the age of the
reading itself (Reading.Timestamp): "Messwert: vor N min". - Staleness keys off data arrival, not measurement age — PEGELONLINE
publishes with a multi-minute lag, so a healthy feed is always minutes-old; an
age-based threshold would flag a permanent false "stale". A tile now turns
stale only when new data stops flowing — measured from when the last
reading arrived, against a threshold derived from the inferred source cadence
(2× cadence, with a floor for the dashboard's own poll interval). So the
Simulator (~seconds) and PEGELONLINE (~minutes) get the right threshold
automatically, with nothing hard-coded. - Wall-clock time anchor in the header — a running clock (
HH:MM:SS,
Europe/Berlin) next to the live indicator, so a measurement's age reads against
the current time: current time → measurement N min old → status. Client-side,
updated every second, no API call. - Local dashboard works on a fresh clone —
config.jsonships the deploy
placeholderhttps://<INGESTION_FQDN>, substituted only at deploy time.
AppConfignow treats an unresolved placeholder as same-origin, so following
the local runbook reaches the dev-proxied/apiinstead of a dead host.
What's changed
Freshness (ingestion + dashboard)
- The gauge API exposes three new fields per gauge:
measuredAt(the newest
reading's measurement time, source clock),cadenceSeconds(the inferred
source cadence — the median gap between reading timestamps, so a single
missed poll does not inflate it), andlastReadingAt(when that reading
arrived, on our clock). - The state holder records each gauge's last arrival time; the API mapper derives
the cadence. State stays raw — view concerns live in the mapper (ADR-002). - The dashboard shows a per-tile freshness chip from
measuredAt, classifies
stale from arrival recency vs the cadence-derived threshold, and drives all
relative times from one shared 1 Hz clock (no per-component timers). - The header indicator is now poll-liveness only (
live/veraltet/
verbinde…); all measurement times moved into the tiles.
Config / local dev
AppConfignormalizes an empty or unresolved"<…>"apiBaseUrlto
same-origin/api; a real FQDN is kept (trailing slash trimmed). The deploy
substitution (sedin the workflow / theazure.yamlhook) is unchanged, so
production still serves the real ingestion FQDN — the change only affects the
pre-substitution (local) case.
Tests
- Mapper:
measuredAt, mediancadenceSeconds(incl. robustness to a missed
interval), andlastReadingAtpass-through. - Freshness threshold: PEGELONLINE
2× cadence, no flapping at a normal
interval, Simulator's few-second window, and unknown-cadence handling. AppConfig: placeholder, empty, real FQDN, and fetch-error fallback.
Known limitations (by design)
- PEGELONLINE readings are inherently minutes old — the public feed publishes
with a multi-minute lag. The dashboard now shows this honestly: the age can sit
at several minutes while the tile stays "live", because new data keeps arriving
on cadence. Stale means the feed has actually stalled. - Dashboard remains read-only and polling-based.
- No notification delivery — alert events are published but acting on them is
still out of scope. - OpenTelemetry is wired but not surfaced — the M8 dashboard observability
work was attempted and reverted; the trace path still exports to Jaeger/OTLP,
just without an in-dashboard view.
What's next
The roadmap beyond M9 is open. The core pipeline, real data feed, deploy paths
(k8s + Argo CD, Container Apps), and now legible freshness are in place; see
docs/ISSUES.md
for the milestone history and any new work.