# Pro Runtime Hardening Workboard Status: living document Branch: `feature/pro-runtime-hardening` Dieses Dokument ist das **Arbeitsdokument** zur schrittweisen Umsetzung des Konzepts aus `fm-rds-tx_pro_runtime_hardening_concept.json`. Ziel ist **nicht** nur eine hübsche Roadmap, sondern ein Ort, an dem wir konkret markieren können: - **wo** wir im Code stehen, - **welche Lücken** bestätigt sind, - **welche Entscheidungen** gefallen sind, - **welche Arbeiten** offen / in Arbeit / erledigt sind, - **welche Risiken** noch bestehen, - **welche Akzeptanzkriterien** wirklich nachgewiesen wurden. --- ## 1. Arbeitsregeln für dieses Dokument ### Statuswerte - `TODO` → noch nicht begonnen - `IN PROGRESS` → aktiv in Arbeit - `BLOCKED` → sinnvoll erkannt, aber blockiert - `DONE` → umgesetzt - `VERIFIED` → umgesetzt **und** sinnvoll geprüft - `DEFERRED` → bewusst nach hinten verschoben - `REJECTED` → bewusst verworfen ### Nachweispflicht Ein Punkt gilt erst als wirklich fertig, wenn eingetragen ist: 1. **Code-Ort(e)** 2. **Was geändert wurde** 3. **Wie verifiziert wurde** 4. **Welche Restrisiken bleiben** ### Update-Regel Wenn wir an einem Workstream arbeiten, soll dieses Dokument mitgezogen werden. Kein „ist im Kopf klar“. Der Stand kommt hier rein. --- ## 2. Gesamtüberblick ## Gesamtstatus - Projektphase: `Umsetzung (WS-01)` - Technischer Fokus aktuell: `Entkoppelter TX-Pfad (FrameQueue + Writer)` - Nächster sinnvoller Startpunkt laut Konzept: `WS-01 Deterministische Echtzeit-TX-Pipeline mit entkoppeltem Writer` - Vorangegangene Workstreams: `WS-03 Semantische Korrektheit und konsistent angewandte Config` (abgeschlossen) ## Repo-bezogene bestätigte Ausgangslage | Thema | Status | Notiz | |---|---|---| | TX-Engine aktuell als synchroner Single-Loop | CONFIRMED | `internal/app/engine.go` | | Persistenter DSP-Zustand im Generator vorhanden | CONFIRMED | `internal/offline/generator.go` | | HTTP-Control vorhanden | CONFIRMED | `internal/control/control.go` | | Config-Validation vorhanden, aber nicht überall semantisch konsistent | CONFIRMED | `internal/config/config.go` + Runtime-Pfade | | Device/Capability-Modell vorhanden, aber noch nicht streng genug | CONFIRMED | `internal/platform/soapy.go` | | Lock-freier SPSC-Audio-Ringbuffer vorhanden | CONFIRMED | `internal/audio/stream.go` | ## Bereits bekannte bestätigte Inkonsistenzen | ID | Status | Beschreibung | Ort | |---|---|---|---| | CFG-SEM-001 | CONFIRMED | `fm.outputDrive` wird in Validation und Runtime nicht konsistent behandelt | `internal/config/config.go`, `internal/app/engine.go` | | CTL-UX-001 | RESOLVED | `handleAudioStream()` beschreibt `--audio-http`; der CLI-Schalter ist nun vorhanden und setzt den Stream-Puffer für `/audio/stream` direkt. | `internal/control/control.go`, `cmd/fmrtx/main.go` | --- ## 3. Prioritätenmodell | Priorität | Bedeutung | |---|---| | P0 | Technische Perfektion und Determinismus | | P1 | Betriebssicherheit und Fehlerbeherrschung | | P2 | Hardware-Wahrheit und RF-Qualität | | P3 | Sichere und saubere Runtime-Steuerung | | P4 | Deployment-, Release- und Service-Reife | --- ## 4. Umsetzungstracker nach Workstream # WS-03 — Semantische Korrektheit und harte Config-/Runtime-Konsistenz **Priorität:** P0 **Gesamtstatus:** IN PROGRESS ## Ziel Ein einziger, eindeutig definierter Parameterraum. Jeder Wert hat exakt eine Bedeutung und identische Constraints in Config, HTTP-API, Runtime und Telemetrie. ## Warum dieser Workstream zuerst Wenn Semantik und Grenzwerte nicht sauber vereinheitlicht sind, bauen spätere Runtime- und Fault-Mechanismen auf unstabilem Fundament. ## Aufgaben ### WS-03-T1 — Parameterinventar erstellen - **Status:** VERIFIED - **Owner:** Builder A - **Code-Orte:** - `internal/config/config.go` - `internal/app/engine.go` - `internal/control/control.go` - `internal/offline/generator.go` - **Ziel:** Alle öffentlich und intern verwendeten Parameter inventarisieren mit: - Name - Typ - Einheit - Bereich - Default - hot-reload-fähig ja/nein - safety class - Telemetrie-Name - **Offene Fragen:** - Wo leben heute implizite Parameter, die nicht sauber dokumentiert sind? - Welche Runtime-Werte sind abgeleitet statt direkt konfigurierbar? - **Nachweis:** - `docs/ws-03-parameter-inventory.md` enthält das inventarisierte Parameter-Tableau und referenziert Config/Control/Engine. - Live-Nutzung über `internal/control/control.go` → `LivePatch` dokumentiert. - **Restrisiken:** - versteckte Semantik in Helper-Funktionen übersehen ### WS-03-T2 — Validation vereinheitlichen - **Status:** VERIFIED - **Owner:** Builder A - **Code-Orte:** - `internal/config/config.go` - `internal/app/engine.go` - `internal/app/engine_test.go` - `internal/control/control.go` - **Ziel:** `Config.Validate()`, Runtime-Update-Pfade und API-Patch-Validierung dürfen nicht divergieren. - **Bereits bekannter Startpunkt:** - `fm.outputDrive` - **Nachweis:** - CFG-SEM-001: `outputDrive`-Validation in `Engine.UpdateConfig` jetzt 0..10 (wie `Config.Validate`). - Tests (`go test ./...`) fangen neue Range ab und besitzen aktualisierten `engine_test`-Check. - Live-Patch fließt durch `txBridge` und `LivePatch` (control) → `LiveConfigUpdate`. - **Restrisiken:** - weitere Inkonsistenzen erst beim Inventar sichtbar ### WS-03-T3 — DesiredConfig / AppliedConfig einführen - **Status:** IN PROGRESS - **Owner:** Lead Coderaffe - **Code-Orte:** - `internal/app/engine.go` - `internal/control/control.go` - ggf. Config-/Statusmodelle - **Ziel:** API und Runtime sollen trennen zwischen: - gewünschter Konfiguration - tatsächlich angewandter Konfiguration - aktuellem Runtime-Zustand - **Nachweis:** - `internal/control/control.go` wartet mit Snapshot-Updates, bis LivePatch erfolgreich war. - `internal/control/control_test.go` deckt ab, dass abgelehnte Live-Updates keine neue `GET /config`-Ansicht schreiben. - **Restrisiken:** - Die API liefert noch nicht beide Sichten gleichzeitig; weitere Workstreams müssen Desired/Applied explizit zurückgeben. ## WS-03 Entscheidungslog | Datum | Entscheidung | Notiz | |---|---|---| | 2026-04-05 | CFG-SEM-001: `fm.outputDrive` | Live-Validierung auf 0..10 angeglichen, Tests angepasst, Parameterinventar dokumentiert. | | 2026-04-05 | WS-03-T3: Desired/Applied-Gate | Control-API zeigt Snapshots nur noch, wenn LivePatch erfolgreich angewendet wurde; Tests verhindern irreführende Wunschwerte. | ## WS-03 Verifikation | Datum | Fokus | Ergebnis | |---|---|---| | 2026-04-05 | `go test ./...` | ✅ Bestätigt `Engine.UpdateConfig`, `LivePatch` und Parameter-Range sowie Inventar-Dokumentation. Neue Control-Tests sichern Desired/Applied-Gate. | --- # WS-01 — Deterministische Echtzeit-TX-Pipeline mit entkoppeltem Writer **Priorität:** P0 **Gesamtstatus:** IN PROGRESS ## Ziel Generator/Upsampler und Hardwarewriter werden als getrennte Stufen mit kleinem, kontrolliertem Frame-Puffer betrieben. ## Aktueller Stand - Der TX-Pfad ist laut Konzept aktuell noch synchron gekoppelt: `GenerateFrame -> optional FMUpsampler.Process -> driver.Write` - Das ist elegant, aber nicht pro-level-hart gegenüber Write-Spikes und Blockaden. ## Aufgaben ### WS-01-T1 — FrameQueue einführen - **Status:** VERIFIED - **Owner:** Lead Coderaffe - **Code-Orte:** - `internal/output/frame_queue.go` - `internal/output/frame_queue_test.go` - `internal/app/engine.go` - **Ziel:** Bounded Queue mit fester Kapazität, sichtbarem Füllstand, Counter- / Statistikzugriff und klarer Trennung zwischen Generator und Writer. - **Zu entscheiden:** - Puffern vor oder nach Upsampling → Device-Frame-Ebene (Queue lebt nach dem Upsampler) für Writer-Simplifizierung. - Referenzkapazität: `runtime.frameQueueCapacity` (default 3) bleibt konfigurierbar. - **Akzeptanzpunkte:** - Keine unbounded Queue. - Fill-Level (High/Low) ist aus `QueueStats` sichtbar. - Queue-Health-Indikator (`queue.health`) liefert `critical`, `low` oder `normal` aus dem Fill-Level. EngineStats.`queue` zeigt den Status ebenfalls. - Drop/Repeat/Mute-Counter sind vorhanden und testbar. - **Nachweis:** - `FrameQueue`-Implementierung (`internal/output/frame_queue.go`) liefert kapazitätsgesteuerte Push/Pop-Logik und Counters. - Engine-Run nutzt Queue vor dem Writer und zeigt `QueueStats` in `EngineStats`. - Tests (`internal/output/frame_queue_test.go` + `go test ./...`) decken Push/Pop, Timeout-Counters, Stats und den neuen Queue-Health-Indikator ab. - **Restrisiken:** - Die Queue wird aktuell synchron getrieben; ein dedizierter Writer-Worker fehlt noch. - Queue-Close erwartet, dass Generator/Writer vor dem Schließen stoppen, sonst droht Panik beim Schreiben. ### WS-01-T2 — Writer-Worker einführen - **Status:** VERIFIED - **Owner:** Lead Coderaffe - **Code-Orte:** - `internal/app/engine.go` (run loop, `writerLoop`, `cloneFrame`, Stats) - `internal/dsp/*` (FMUpsampler / Resampler copy `GeneratedAt` für Cycle-Metriken) - **Ziel:** Generator/Upsampler liefern Frames in die FrameQueue, `driver.Write()` läuft nur noch im dedizierten Writer. - **Akzeptanzpunkte:** - `writerLoop()` ist die einzige Stelle mit `driver.Write()` und zieht aus der Queue. - FrameQueue ist ein echter Puffer (Generator klont Frames, Writer poppt) und `EngineStats.Queue` zeigt den Füllstand. - Write- und Cycle-Latenzen plus `LateBuffers` bleiben in `EngineStats` sichtbar (`MaxWriteMs`, `LateBuffers`, `MaxCycleMs`). - **Nachweis:** - `go test ./...` (Engine + Queue + DSP) läuft erfolgreich. - `EngineStats` berichtet weiterhin über Queue-/Writer-Metriken. - **Restrisiken:** - Frame-Klonierung pro Chunk erhöht Heap-Pressure; spätere Workstreams sollten Pooling / Zero-Copy prüfen. ### WS-01-T3 — Supervisor-Schicht einführen - **Status:** IN PROGRESS - **Owner:** Lead Coderaffe - **Code-Orte:** - `internal/app/engine.go` - **Ziel:** Queue-Füllstand, Late-Rate und Fehlerhäufigkeit überwachen und in explizite Runtime-Zustände überführen, sodass ein degradierter Queue-Health-Pfad automatisch auf `degraded`, `muted` oder `faulted` zeigt. - **Akzeptanzpunkte:** - Alle Runtime-Entscheidungen laufen über `evaluateRuntimeState`, nicht stillschweigend weiter auf `running`. - Queue-Health, Late-Buffers und Fault-Events treiben gezielt `degraded` → `muted` → `faulted`, damit Operatoren wissen, wann Blockaden vorliegen. - `EngineStats` und `/runtime` bringen `runtimeIndicator`, `queue`, `faultHistory`, `transitionHistory` und das `runtimeState`-Label, so Telemetrie und UI dieselben Signale sehen. - **Nachweis:** - `internal/app/engine.go` (Generator-/Writer-Loops) ruft `evaluateRuntimeState` auf und protokolliert Fault-Events, Transition-Historien und Counters. - `txBridge.TXStats` (`cmd/fmrtx/main.go`) leitet die Runtime-Infos an `/status` und `/runtime`, damit die API-Layer aktuelle Fault-Zustände spiegeln. - `internal/app/runtime_state_test.go` plus `go test ./...` sichern die erwarteten Transition-Reihenfolgen und Fault-Counter. - **Restrisiken:** - Queue-Schwellen für `critical`/`lateBuffers` brauchen noch Feldvalidierung und ggf. Konfiguration. - Fault-Reset/Operator-Interaktion ist im Control-Plane-UI noch zu finalisieren. ## Offene Architekturfragen - Ist `capacity_frames = 3` ein guter Startwert oder nur Konzept-Default? - Sollte im Fault-Fall `repeat last safe frame` erlaubt sein oder von Anfang an nur `mute`? - Wie eng koppeln wir WS-01 mit WS-02, ohne Overengineering zu erzeugen? ## WS-01 Entscheidungslog | Datum | Entscheidung | Notiz | |---|---|---| | 2026-04-05 | FrameQueue mit Engine-Integration | Queue lebt nach dem Upsampler auf DeviceFrame-Ebene, Kapazität via `runtime.frameQueueCapacity`, `EngineStats` zeigt `QueueStats`, Tests decken Timeouts und Counters ab. | | 2026-04-05 | Queue-Health-Indikator | `QueueStats.Health` gibt `critical`/`low`/`normal` zurück und `txBridge` leitet `EngineStats.Queue` ins `/runtime`-JSON. | | 2026-04-05 | Runtime-Indikator | `EngineStats.RuntimeIndicator` kombiniert `queue.health` + `lateBuffers`, `/runtime` zeigt `engine.runtimeIndicator`. | | 2026-04-05 | /status runtime indicator | `/status` reuses `txBridge.TXStats()` and now reports `runtimeIndicator` alongside the config snapshot for quick ops. | | 2026-04-05 | /status queue stats | `/status` spiegelt das `queue`-Objekt aus `txBridge.TXStats()` für schnelle Queue-Checks, API-Doku und `TestStatusReportsQueueStats` fangen den neuen Key ab. | ## WS-01 Verifikation | Datum | Fokus | Ergebnis | |---|---|---| | 2026-04-05 | FrameQueue + Engine integration | ✅ `go test ./...` (im `internal`-Modul incl. `frame_queue_test.go`) | | 2026-04-05 | Queue-Health-Indikator | go test ./... deckt `TestFrameQueueHealthIndicator` und `queue.health` ab. | | 2026-04-05 | Runtime-Indikator | OK `go test ./...` deckt `runtimeIndicator` sowie `/runtime`-Exposition von `engine.runtimeIndicator`. | | 2026-04-05 | Runtime API queue health | ✅ `/runtime` liefert jetzt `engine.queue.health` dank `txBridge.TXStats`. | | 2026-04-05 | /status runtime indicator | ✅ `/status` gibt jetzt `runtimeIndicator` aus (`control_test` deckt den neuen Key). | | 2026-04-05 | /status queue stats | ✅ `TestStatusReportsQueueStats` plus `docs/API.md` zeigen, dass `queue` korrekt durchgereicht wird. | --- # WS-02 — Explizite Runtime-State-Maschine und Fault-Handling **Priorität:** P0 **Gesamtstatus:** IN PROGRESS ## Ziel Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständen. ## Fortschritt - EngineStats liefert das Runtime-State-Feld (`idle`, `arming`, `prebuffering`, `running`) und reagiert nun auf Queue-Gesundheit bzw. späte Buffers, indem es bei `low`/`critical` oder späten Buffern in `degraded` wechselt und sonst auf `running` zurückkehrt. - `evaluateRuntimeState` escalates persistent `critical` queues from `degraded` to `muted`, while `FaultReasonQueueCritical` surfaces `muted` severity so the mute transition stays observable. - `evaluateRuntimeState` now waits for a short healthy streak before leaving `muted`, logging a degraded-severity recovery event once the queue settles. - Persistent queue-critical streaks while `muted` now escalate to `faulted` with `FaultSeverityFaulted`, keeping `RuntimeStateFaulted` observable. - `EngineStats` and `txBridge` now expose transition/fault counters plus `lastFault`, surfacing the new telemetry through `/runtime`. - Control-plane UI now renders those WS-02 transition counters, fault count, and last-fault summary so operators can watch runtime escalations without digging through logs. - Control-plane now exposes `POST /runtime/fault/reset` so operators can acknowledge `faulted` state; `TestRuntimeFaultReset*` covers the new HTTP path. - Control-plane UI now also offers a Danger Zone `Reset Fault` button that calls the same endpoint so operators can acknowledge faults from the dashboard. - Control-plane UI now posts an ops toast/log entry whenever the runtime state shifts so escalations and manual acknowledgements are immediately visible. - Control-plane UI now keeps a compact Transition History panel beside the Fault History so operators can see recent runtime shifts without scrolling the activity log. ## Zielzustände laut Konzept - `idle` - `arming` - `prebuffering` - `running` - `degraded` - `muted` - `faulted` - `stopping` ## Aufgaben ### WS-02-T1 — Fault-Klassifikation definieren - **Status:** IN PROGRESS - **Owner:** Lead Coderaffe - **Beispiele:** - `queueCritical` - `lateBuffers` - `writeTimeout` (z. B. Driver-Timeouts) - `queueEmpty` - `unknown` (Catch-all für unvorhergesehene Runtime-Zustände) - **Ziel:** Alle relevanten Fehlertypen als `FaultReason`/`FaultSeverity` codieren, damit sie später eindeutig auf Telemetrie und Logs abgebildet werden können. - **Nachweis:** - `internal/app/fault.go` definiert Reasons (`queueCritical`, `lateBuffers`, `writeTimeout`, `queueEmpty`, `unknown`) und Severity-Stufen (`warn`, `degraded`, `muted`, `faulted`). - `internal/app/engine.go` ruft `recordFault` im Queue- und Late-Buffer-Prozess auf, so dass jede Reason vom Fault-Historien-Log erfasst wird. - `internal/app/runtime_state_test.go` und `internal/app/fault_test.go` prüfen, dass die Reason/Severity-Kombinationen korrekt geloggt und ausgewertet werden. - **Restrisiken:** Weitere Driver-/Hardware-Faults (z. B. Soapy-Timeouts oder Audio-Stream-Abbrüche) müssen noch explizit getriggert und klassifiziert werden. ### WS-02-T2 — Reaktionsstrategie definieren - **Status:** IN PROGRESS - **Owner:** Lead Coderaffe - **Ziel:** Reaktionen für jede FaultSeverity klar definieren (warn → loggen, degraded → degrade state, muted → stilllegen, faulted → Reset-Hürde). - warn only - degraded - muted - faulted - **Nachweis:** - `evaluateRuntimeState` eskaliert queueCritical-Läufe zuerst zu `degraded`, dann `muted`, schließlich `faulted` und protokolliert die entsprechenden Severity-Labels. - `Engine.ResetFault()` bringt `faulted` deterministisch zurück auf `degraded`, damit die Supervisor-Logik das Manual-Reset respektiert. - Tests in `internal/app/runtime_state_test.go` prüfen, dass die Transition-Counter (`degradedTransitions`, `mutedTransitions`, `faultedTransitions`) und `faultCount` bei den richtigen Ereignissen springen. - **Restrisiken:** Die aktuellen Schwellen basieren auf queueCritical-Streaks; zusätzliche FaultSources (Driver, Audio-Stream, Live-Update-Rejection) brauchen eigene Severity-Strategien. ### WS-02-T3 — Fault-Historie und Event-Log einführen - **Status:** IN PROGRESS - **Owner:** Lead Coderaffe - **Ziel:** Zustandswechsel, Fault-Count und Trace-Historien auditierbar machen, damit `/runtime` und die UI eine nachvollziehbare Story liefern können. - **Nachweis:** - `EngineStats` enthält `faultHistory`, `transitionHistory`, `lastFault`, `faultCount` sowie `runtimeStateDurationSeconds` und Runtime-Indikatoren. - `txBridge.TXStats` leitet diese Infos in `/runtime` und `/status` weiter, `internal/control/control_test.go` sichert, dass `faultHistory` und `transitionHistory` korrekt serialisiert werden. - `internal/app/runtime_state_test.go` validiert die Historienkapazität, `go test ./...` deckt die API-Exposition ab. - **Restrisiken:** Die History-Kapazität ist auf 8 Einträge begrenzt; ein Audit-Log-Backend könnte später die Lücke auffangen. ## Offene Designfragen - Wie fein granular darf die State-Maschine werden, ohne unwartbar zu werden? - Welche Transitionen sind wirklich produktiv relevant und welche nur „theoretisch schön“? ## WS-02 Entscheidungslog | Datum | Entscheidung | Notiz | |---|---|---| | 2026-04-05 | Faulted escalation on persistent critical queue | `muted` now surfaces `RuntimeStateFaulted` when queue health stays critical and metrics capture every transition. | | 2026-04-05 | Manual fault reset endpoint | Added `POST /runtime/fault/reset` so operators can acknowledge `faulted` before the supervisor re-enters recovery. | | 2026-04-05 | Fault-reset UI shortcut | Danger Zone now hosts a Reset Fault button wired to `/runtime/fault/reset` so operators get an in-app acknowledgement path without manual HTTP calls. | | 2026-04-06 | Runtime transition visibility cue | Control UI now posts toast/log entries for runtime state shifts so ops instantly sees escalations and manual reset acknowledgements. | | 2026-04-06 | Transition history panel | Added a compact Transition History panel next to the Fault History so the last few runtime state shifts stay visible even when the activity log is full. | ## WS-02 Verifikation | Datum | Fokus | Ergebnis | |---|---|---| | 2026-04-05 | Faulted path + transition counters | `go test ./...` exercises `TestEngineFaultsAfterMutedCriticalStreak` and `TestRuntimeTransitionCounters`, while `/runtime` now surfaces `engine.degradedTransitions`, `engine.mutedTransitions`, `engine.faultedTransitions`, `engine.faultCount`, and the last fault via `txBridge`. | | 2026-04-05 | Runtime fault reset API | `go test ./...` now runs `TestRuntimeFaultReset*`, verifying the new HTTP path and controller error scenarios. | | 2026-04-06 | Runtime transition visibility | ✅ `go test ./...`; manual UI smoke verification still pending to ensure the toast/log flow shows every runtime shift. | --- # WS-04 — Observability, Telemetrie und Diagnosefähigkeit **Priorität:** P1 **Gesamtstatus:** TODO ## Ziel Vollständige Sichtbarkeit auf Runtime, Queue, Writer, Generator, RF-Selbsttests und API-Aktivität schaffen. ## Aufgaben ### WS-04-T1 — Strukturiertes Logging - **Status:** TODO - **Owner:** offen ### WS-04-T2 — Prometheus-/Metrics-Schicht - **Status:** TODO - **Owner:** offen ### WS-04-T3 — Debug-/Profiling-Endpunkte - **Status:** TODO - **Owner:** offen ## Gewünschte Beispielmetriken - `engine_chunks_generated_total` - `engine_late_buffers_total` - `engine_fault_transitions_total` - `writer_write_duration_seconds` - `queue_fill_ratio` - `queue_dropped_frames_total` - `queue_muted_frames_total` - `driver_write_errors_total` - `audio_stream_underruns_total` - `audio_stream_overflows_total` - `rf_selftest_pilot_db` - `rf_selftest_rds_57k_db` ## WS-04 Entscheidungslog | Datum | Entscheidung | Notiz | | --- | --- | --- | | 2026-04-06 | High-watermark trend sparkline | Captured audio high-watermark duration history and surface it as a new Health-panel sparkline for queue pressure visibility. | | 2026-04-06 | Queue fill visibility | Added queue fill ratio health line and sparklines to highlight real-time queue pressure alongside high-watermark trends. | | 2026-04-07 | Underrun streak telemetry | StreamStats now expose current and max underrun streak counters so queue diagnostics can see repeated underruns without touching the metrics stack. | ## WS-04 Verifikation | Datum | Fokus | Ergebnis | | --- | --- | --- | | 2026-04-06 | High-watermark trend sparkline | `go test ./...` plus manual UI check confirm the new sparkline updates with runtime audio stats. | | 2026-04-06 | Queue fill visibility | `go test ./...` plus UI smoke check confirm queue fill stats stay available and the new sparkline/health line react to queue health changes. | | 2026-04-07 | Underrun streak telemetry | `go test ./internal/audio` confirms the new streak counters plus Stats coverage so the API surfaces the same names. | --- # WS-05 — Sichere und erwachsene Control-Plane **Priorität:** P1 / P3-nah **Gesamtstatus:** TODO ## Ziel API transport- und anwendungsseitig härten, state-aware machen und auditierbar gestalten. ## Aufgaben ### WS-05-T1 — Auth und Deploy-Modi definieren - **Status:** TODO - **Owner:** offen - **Zielmodi:** - localhost-only - trusted-lan - secured-remote ### WS-05-T2 — HTTP-Server härten - **Status:** TODO - **Owner:** offen - **Mindestpunkte:** - ReadTimeout - WriteTimeout - IdleTimeout - ReadHeaderTimeout - Body-Size-Limits - Content-Type-Validierung - Method Enforcement ### WS-05-T3 — API semantisch aufräumen - **Status:** TODO - **Owner:** offen - **Ziel:** - DesiredConfig vs AppliedConfig vs RuntimeState - idempotente Start/Stop-Endpunkte - transaktionsartige Apply-/Reject-Antworten - Audit-Log pro Eingriff ## Frühe Quick-Wins Diese Punkte könnten ggf. vorgezogen werden, auch wenn WS-05 formal nach WS-01/02 kommt: - HTTP-Timeouts - Body-Limits - sicherer Standard-Bind-Modus ## WS-05 Entscheidungslog - 2026-04-06: `/audio/stream` now enforces a binary `Content-Type` (`application/octet-stream` or `audio/L16`) before queuing any samples. - 2026-04-06: `/audio/stream` caps uploads at 512 MiB and rejects larger bodies with `413 Request Entity Too Large` before touching the ring buffer. ## WS-05 Verifikation | Datum | Fokus | Ergebnis | |---|---|---| | 2026-04-05 | `/audio/stream` rejects non-POST requests | `TestAudioStreamRejectsNonPost` enforces POST-only access to `/audio/stream` before a stream source is configured | | 2026-04-06 | `/audio/stream` enforces binary Content-Type headers | `TestAudioStreamRejectsMissingContentType` and `TestAudioStreamRejectsUnsupportedContentType` confirm 415 when the media type is missing or wrong | | 2026-04-06 | `/audio/stream` rejects oversized uploads | `TestAudioStreamRejectsBodyTooLarge` confirms a 413 Request Entity Too Large before buffering when the HTTP body exceeds the 512 MiB guard | --- # WS-06 — Hardware-in-the-loop und externe RF-Wahrheitsprüfung **Priorität:** P2 **Gesamtstatus:** TODO ## Ziel Nicht nur intern richtig rechnen, sondern extern nachweisen, dass tatsächlich korrekt gesendet wird. ## Status - Konzept vorhanden - noch kein eingetragener HIL-Arbeitsstand in diesem Dokument ## Offene Kernfragen - Welches Referenz-Setup wird verbindlich? - Welche Testfrequenz / Standarddauer / Schutzmaßnahmen gelten? - Welcher externe Decoder / Empfänger gilt als Referenz? --- # WS-07 — Device-aware Capability- und Kalibrierungsmodell **Priorität:** P2 **Gesamtstatus:** TODO ## Ziel Fähigkeiten und Kalibrierungen nicht implizit, sondern explizit pro Device modellieren. ## Noch offen - Capability-Schema konkretisieren - Kalibrierungsprofil definieren - Device-aware Validation einbauen --- # WS-08 — Signal-Selbstüberwachung im Betrieb **Priorität:** P2 **Gesamtstatus:** TODO ## Ziel Pilot, Stereo, RDS und Composite-Anomalien im Betrieb erkennen. ## Noch offen - Goertzel/FFT-Strategie festlegen - Schwellwerte definieren - in Fault-Logik einspeisen --- # WS-09 — Teststrategie erweitern **Priorität:** P3/P4-nah **Gesamtstatus:** TODO ## Ziel Von Unit-Tests zu echter Qualitätsabsicherung: Golden Vectors, Long-Run, Race, Fuzzing, API-Mutation, HIL. ## Noch offen - Testpyramide konkretisieren - Nightly-/CI-Fähigkeit bestimmen --- # WS-10 — Service-Reife, Packaging und Reproduzierbarkeit **Priorität:** P4 **Gesamtstatus:** TODO ## Ziel Build-, Release- und Betriebsartefakte reproduzierbar und teamtauglich machen. ## Noch offen - Build-Metadaten - Service-Units - Config-Versionierung / Migration --- ## 5. Übergreifende Regeln ### Musts - Jeder neue Runtime-Zustand muss per API und Telemetrie sichtbar sein. - Jede Recovery-, Drop- oder Mute-Strategie braucht Counter, Logs und Tests. - Keine neue Config-Option ohne klaren Typ, Bereich, Einheit, Default und Hot-Reload-Klassifikation. - Hardware-nahe Änderungen brauchen mindestens Simulations- und HIL-Validierung. - Alle Faults müssen eine maschinenlesbare Ursache und eine menschenlesbare Zusammenfassung haben. ### Must Not - Keine unbounded Queues. - Keine stillen Fallbacks ohne Telemetrie. - Keine teilweise angewandten Live-Config-Änderungen ohne explizite Rückmeldung. - Keine unterschiedlichen Grenzwerte zwischen Config, API und Runtime. - Keine sicherheitsrelevanten HTTP-Endpunkte ohne Härtung im Remote-Betrieb. --- ## 6. Aktuelle offene Entscheidungen | ID | Status | Frage | Notiz | |---|---|---|---| | DEC-001 | RESOLVED | Puffern wir auf CompositeFrame- oder DeviceFrame-Ebene? | Queue lebt nach dem Upsampler (DeviceFrame-Ebene) gemäß `internal/app/engine.go`-Integrationsschleife. | | DEC-002 | OPEN | Fault-Recovery zuerst mit `mute`, `repeat last safe frame` oder beidem? | Muss technisch und RF-seitig sauber bewertet werden | | DEC-003 | OPEN | Ziehen wir minimale WS-05-Basis-Härtungen vor? | Timeouts/Body-Limits evtl. früher sinnvoll | | DEC-004 | OPEN | Wie gross/simpel halten wir die erste State-Maschine? | Gefahr von Overengineering | --- ## 7. Nächste sinnvolle Schritte ### Empfohlener Start 1. **WS-03-T1 Parameterinventar erstellen** *(abgeschlossen)* 2. **bekannte Inkonsistenzen (CFG-SEM-001, CTL-UX-001) konkret verifizieren** 3. **DesiredConfig / AppliedConfig / RuntimeState Zielmodell grob skizzieren** 4. Danach Architekturarbeit an **WS-01 + WS-02** starten 5. **Aktuell:** WS-01-T2 Writer-Worker einführen (Queue → Driver), danach WS-01-T3 Supervisor + WS-02 Runtime-State. ### Vor dem ersten grossen Umbau klären - Was ist „minimal sinnvoll“ für Milestone 1? - Welche Dinge sind harte Must-haves und welche nur spätere Veredelung? - Wo wollen wir bewusst nicht sofort maximal abstrahieren? --- ## 8. Änderungsprotokoll | Datum | Änderung | Person / Agent | |---|---|---| | 2026-04-05 | Initiales Arbeitsdokument aus `fm-rds-tx_pro_runtime_hardening_concept.json` erstellt | Alfred |