Skip to content

v1.3.0 — Readability

Latest

Choose a tag to compare

@goldbarth goldbarth released this 16 Jun 14:35
cfa78be

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 cloneconfig.json ships the deploy
    placeholder https://<INGESTION_FQDN>, substituted only at deploy time.
    AppConfig now treats an unresolved placeholder as same-origin, so following
    the local runbook reaches the dev-proxied /api instead 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), and lastReadingAt (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

  • AppConfig normalizes an empty or unresolved "<…>" apiBaseUrl to
    same-origin /api; a real FQDN is kept (trailing slash trimmed). The deploy
    substitution (sed in the workflow / the azure.yaml hook) is unchanged, so
    production still serves the real ingestion FQDN — the change only affects the
    pre-substitution (local) case.

Tests

  • Mapper: measuredAt, median cadenceSeconds (incl. robustness to a missed
    interval), and lastReadingAt pass-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.