# 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 |
