feat(dashboard): Eingangs-Status „Simulation (intern)" an Live-Stream koppeln - #49
Conversation
… koppeln
Die Plattform-Topologie-Kachel „Simulation (intern)" und das globale
„Live"-Badge spiegeln jetzt denselben datengetriebenen Zustand des
Eingangs-Live-Streams (Zwilling als Datenquelle) statt eines Transport-
Etiketts.
- reads/stream.py (NEU): build_stream_status/classify_stream — aktiv/inaktiv
aus dem jüngsten simulation-Reading gegen STREAM_FRESH_WINDOW (5 min),
die EINE Wahrheit für Kachel UND Badge.
- /overview (FleetOverviewOut) trägt stream:{active,last_reading_at}
(HTTP + WS-Push, scope-unabhängig).
- Live-Badge (global-status-bar): „Live" nur bei offener WS UND tickendem
Stream; sonst „Verlauf" — kein Live-Etikett über statischer Historie.
- Topologie-Kachel: interne Quelle gegen das enge Stream-Fenster gemessen
(konsistent mit dem Badge); FE rendert „aktiv" statt „verbunden"
(Backend-Statusvertrag stabil).
- Verfassung: ehrlicher Zustand, „aktiv" nur wenn wirklich getickt wird.
FOREMANs internes Reasoning-Simulieren (#5) bleibt außen vor.
Gates grün: ruff/mypy --strict 0, pytest 859 passed @94.31%, tsc 0,
ESLint 0, vitest 656 passed. GROUND_TRUTH §4/§20.1/§22.2 nachgezogen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughDer PR ergänzt einen zentralen Stream-Status im Read-Core, erweitert den Overview-/Dashboard-Vertrag um ChangesStream-Status und Anzeige
Sequence Diagram(s)sequenceDiagram
participant build_fleet_overview
participant build_stream_status
participant latest_source_reading
participant AsyncSession
build_fleet_overview->>build_stream_status: stream status anfordern
build_stream_status->>latest_source_reading: source="simulation"
latest_source_reading->>AsyncSession: SELECT max(Reading.time) via DataPoint.source
AsyncSession-->>latest_source_reading: last_reading_at
latest_source_reading-->>build_stream_status: last_reading_at
build_stream_status->>build_stream_status: classify_stream(...)
build_stream_status-->>build_fleet_overview: StreamStatus{active,last_reading_at}
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Regression-Guard (Review-Befund): beweist, dass der jüngste Reading-Stempel ÜBER ALLE simulation-Datenpunkte den Stream-Stand bestimmt (Aggregat-Semantik), nicht ein beliebiger/erster Datenpunkt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/foreman/reads/stream.py`:
- Around line 63-66: The formatting in the read freshness check is not aligned
with ruff format, so update the multi-line conditional expression in the
stream-reading helper to match the formatter’s output. Reformat the stamp
assignment and return logic in the function that compares last_reading_at
against fresh_window so it follows the exact style produced by uv run ruff
format src/foreman/reads/stream.py, preserving behavior while matching CI
formatting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 02474b1d-df39-4067-9e93-a6166cd9eedc
📒 Files selected for processing (25)
GROUND_TRUTH.mdfrontend/components/alarms/alarms-view.test.tsxfrontend/components/atoms/atoms.test.tsxfrontend/components/atoms/provenance-stamp.tsxfrontend/components/cockpit/cockpit-view.test.tsxfrontend/components/platform/topology-graph.test.tsxfrontend/components/platform/topology-graph.tsxfrontend/components/platform/topology-node-mark.tsxfrontend/components/shell/global-status-bar.test.tsxfrontend/components/shell/global-status-bar.tsxfrontend/lib/alarms/counts.test.tsfrontend/lib/api/contracts.tsfrontend/lib/platform/status.test.tsfrontend/lib/platform/status.tsfrontend/lib/ui/stream-freshness.test.tsfrontend/lib/ui/stream-freshness.tssrc/foreman/reads/overview.pysrc/foreman/reads/stream.pysrc/foreman/schemas/dashboard.pysrc/foreman/topology/service.pytests/integration/test_reads_overview.pytests/integration/test_reads_stream.pytests/topology/test_topology.pytests/unit/test_dashboard_schemas.pytests/unit/test_reads_stream.py
CI-Fix: ruff format --check verlangt die geklammerte Mehrzeilen-Form des ternären Ausdrucks. Verhaltensneutral. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ziel
Die Plattform-Topologie-Kachel „Simulation (intern)" und das globale „Live"-Badge spiegeln jetzt denselben datengetriebenen Zustand des Eingangs-Live-Streams (digitaler Zwilling als Datenquelle) — statt eines Etiketts ohne Substanz.
Befund (korrigiert die Auftrags-Prämisse)
Der Status der Kachel war nicht statischer Text:
topology/service.py:_source_nodesleitet ihn längst ausmax(Reading.time)dersimulation-Quelle ab. Das eigentliche Etikett-ohne-Substanz war das „Live"-Badge — es hing am WS-Transport-Zustand, nicht am Stream, und hätte „Live" über rein statischer Historie gezeigt. Diese PR schließt den echten Riss.Änderungen
Backend
reads/stream.py(neu):build_stream_status/classify_stream→ aktiv/inaktiv aus dem jüngstensimulation-Reading gegenSTREAM_FRESH_WINDOW(5 min, kalibriert auf den 60-s-Worker-Takt). Die eine Wahrheit für Kachel UND Badge.GET /api/v1/overview(FleetOverviewOut) trägtstream: {active, last_reading_at}— überbuild_fleet_overview, also HTTP und WS-Push zugleich, scope-unabhängig.topology/service.py: die internesimulation-Quelle wird gegen dasselbe enge Stream-Fenster gemessen wie das Badge (Konsistenz); externe Protokolle behalten das generischefresh_within_minutes.Frontend
global-status-bar: „Live" nur bei offener WS-Verbindung und tickendem Stream; sonst „Verlauf" (ProvenanceStampumhistoryerweitert). Logik als reine FunktionstreamBadgeFreshness.connectionStatusLabel) — Backend-Statusvertrag bleibt stabil, nur die UI-Sprache wird ehrlicher.Konsistenz & Verfassung
last_reading_atgegen dieselbe Schwelle → spiegeln dieselbe Wahrheit. „aktiv"/„Live" nur, wenn der Worker wirklich tickt.Bewusste Design-Entscheidung (server-autoritativ)
Reine Reading-Ticks lösen kein
overview-Reload aus (topics_for_change: Readings → nurtrend). Den Ingest-NOTIFY-Kern dafür anzufassen wäre ein schwerer F3/F5-Eingriff außerhalb des „schlanken" Auftrags. Daher iststream.activeserver-autoritativ zum overview-Load (Seiten-Load / Alarm-Event / Reconnect) — korrekt und ehrlich, ohne clientseitige Alterung, die bei laufendem Worker lügen würde. Ein Stopp wird beim nächsten overview-Load erkannt; die 5-min-Schwelle deckt die typische Latenz.Abnahme
test_topology.py(Stream-Fenster),test_reads_stream.py,test_reads_overview.py.global-status-bar.test.tsx(Verlauf bei inaktivem Stream, Live bei tickendem),stream-freshness.test.ts.max(Reading.time)).Gates (lokal grün)
ruff0 ·mypy --strict0 ·pytest859 passed @ 94.31 % ·tsc0 ·ESLint0 ·vitest656 passed. GROUND_TRUTH §4 / §20.1 / §22.2 im selben Commit nachgezogen.🤖 Generated with Claude Code
Summary by CodeRabbit