diff --git a/fm-rds-tx_pro_runtime_hardening_concept.json b/fm-rds-tx_pro_runtime_hardening_concept.json new file mode 100644 index 0000000..c757a2d --- /dev/null +++ b/fm-rds-tx_pro_runtime_hardening_concept.json @@ -0,0 +1,831 @@ +{ + "document_type": "technical_concept", + "language": "de", + "audience": [ + "AI-Entwicklerteam", + "Reviewer", + "Maintainer" + ], + "project": { + "name": "fm-rds-tx", + "goal": "Aus dem bestehenden FM-Stereo/RDS-TX-System ein technisch sauberes, deterministisches, messbares und betriebsfestes Pro-Level-System machen.", + "primary_priority": "Technische Perfektion", + "secondary_priority": "Sinnvolle Umsetzungsreihenfolge mit maximalem Risikoabbau zuerst" + }, + "executive_summary": { + "current_strengths": [ + "Saubere Modultrennung zwischen Generator, DSP, Control, Audio, Backend und Plattform.", + "Persistenter DSP-Zustand ist bereits vorhanden, insbesondere im Generator und im FM-Upsampler.", + "Live-Updates werden bereits über atomare Snapshots bzw. Pointer modelliert.", + "Es existieren bereits viele Unit-Tests sowie spektrale Blackbox-Tests für 19 kHz, 38 kHz und 57 kHz." + ], + "current_limitations": [ + "Der TX-Pfad in internal/app/engine.go ist noch ein einzelner synchroner Generate/Upsample/Write-Loop ohne entkoppelten Echtzeitpuffer.", + "Die Runtime-Recovery ist schwach: bei Fehlern wird nur gezählt, geloggt und gewartet; es gibt keinen expliziten Fault-State mit deterministischem Fallback.", + "Die Control-Plane in internal/control/control.go ist funktional, aber nicht hart genug: keine Authentisierung, keine Transporthärtung, keine Request-Limits, keine Timeouts, keine Audit-Trails.", + "Validation und Runtime-Semantik sind nicht überall deckungsgleich; Beispiel: fm.outputDrive wird in config.Validate() bis 10 akzeptiert, in Engine.UpdateConfig() aber nur bis 3.", + "Device-Abstraktion ist brauchbar, aber noch nicht streng genug capability- und kalibrierungsgetrieben.", + "Observability ist noch zu schwach für echten Dauerbetrieb und reproduzierbare Fehleranalyse." + ], + "core_statement": "Der DSP-Kern ist nah an ernsthaft brauchbar. Der Abstand zu Pro-Level liegt primär in Betriebssicherheit, Observability, Hardwarevalidierung und strenger Runtime-Kontrolle." + }, + "repo_grounding": { + "confirmed_code_touchpoints": [ + { + "path": "internal/app/engine.go", + "observation": "TX läuft aktuell in einer einzelnen Goroutine als synchroner Zyklus: GenerateFrame -> optional FMUpsampler.Process -> driver.Write. Kein echter Producer/Consumer-Puffer zwischen Generator und Hardwarewriter." + }, + { + "path": "internal/offline/generator.go", + "observation": "Generator hat bereits persistenten Zustand, LiveParams per atomic.Pointer und sinnvolle DSP-Kette inklusive optionalem BS.412-Limiter." + }, + { + "path": "internal/control/control.go", + "observation": "HTTP-Control existiert bereits, aber ohne sichtbare Authentisierung, ohne Server-Timeout-Konfiguration und ohne harte API-Grenzen." + }, + { + "path": "internal/config/config.go", + "observation": "Validation ist vorhanden, aber semantisch nicht überall konsistent mit Laufzeitregeln." + }, + { + "path": "internal/platform/soapy.go", + "observation": "Capabilities und RuntimeStats existieren als Ansatz, reichen aber noch nicht für eine wirklich harte device-aware Steuerung." + }, + { + "path": "internal/audio/stream.go", + "observation": "Lock-freier SPSC-Ringbuffer für Live-Audio ist bereits vorhanden und kann als Referenz für deterministische Buffer-Designs dienen." + } + ], + "confirmed_inconsistencies": [ + { + "id": "CFG-SEM-001", + "description": "fm.outputDrive wird in Config.Validate() bis 10 akzeptiert, die Fehlermeldung spricht aber von 0..3, und Engine.UpdateConfig() erzwingt tatsächlich 0..3." + }, + { + "id": "CTL-UX-001", + "description": "handleAudioStream() nennt in der Fehlermeldung '--audio-http', im CLI ist dieser Schalter nicht als gleichwertiger offensichtlicher Bedienpfad bestätigt." + } + ] + }, + "design_principles": [ + "Kein verstecktes Glück: Jeder relevante Echtzeit- oder RF-Pfad muss deterministisch, messbar und reproduzierbar sein.", + "Fail-safe statt fail-weird: Bei Unsicherheit oder Überlast lieber definiert muten oder faulten als kaputt weiterzusenden.", + "Eine Runtime-Wahrheit: Konfiguration, Live-State und tatsächlich angewandte Hardware-/DSP-Parameter dürfen nicht auseinanderlaufen.", + "Hardware ist Wahrheit: IQ-Dateien und Unit-Tests reichen nicht; es braucht Hardware-in-the-loop und externe Decoder-/Messvalidierung.", + "Observability ist Pflicht, nicht Luxus: Kein Pro-Level ohne Metriken, strukturierte Logs, Fault-Telemetrie und reproduzierbare Regressionen.", + "Keine implizite Semantik: Alle Parameter müssen in Config, API, Runtime und Telemetrie exakt dasselbe bedeuten." + ], + "priority_model": { + "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" + }, + "workstreams": [ + { + "id": "WS-01", + "priority": "P0", + "title": "Deterministische Echtzeit-TX-Pipeline mit entkoppeltem Writer", + "why": "Der aktuelle Single-Loop ist elegant, aber anfällig gegen Write-Spikes, Scheduling-Jitter und hardwarebedingte Blockaden. Pro-Level verlangt Entkopplung zwischen Erzeugung und Ausgabe.", + "objective": "Generator/Upsampler und Hardwarewriter werden als getrennte Stufen mit kleinem, kontrolliertem Frame-Puffer betrieben. Der Writer darf kurzfristige Timing-Jitter absorbieren, ohne sofort den gesamten TX-Zyklus zu ruinieren.", + "target_architecture": { + "pipeline": [ + "control-plane", + "runtime supervisor", + "generator worker", + "optional upsampler worker", + "bounded frame queue", + "writer worker", + "driver" + ], + "queue_policy": { + "type": "bounded ring queue", + "capacity_frames": 3, + "default_behavior": "Producer füllt vor, Writer sendet in Echtzeit, Supervisor überwacht Queue-Füllstand", + "allowed_strategies": [ + "block producer kurzzeitig", + "repeat last safe frame im Fault-Recovery-Modus", + "mute frame im Sicherheitsmodus" + ], + "forbidden_strategies": [ + "unbounded buffering", + "still und heimlich alte Frames verwerfen ohne Counter/Log", + "dynamisches Verhalten ohne Telemetrie" + ] + } + }, + "implementation_tasks": [ + { + "id": "WS-01-T1", + "title": "FrameQueue einführen", + "details": [ + "Neue interne Queue-Struktur für CompositeFrame oder DeviceFrame einführen.", + "Explizit festlegen, ob vor oder nach FMUpsampler gepuffert wird. Empfehlung: Puffern auf Device-Frame-Ebene, damit der Writer nur noch sendet.", + "FrameQueue muss feste Kapazität, Füllstand, High-Watermark, Low-Watermark und Drop-/Mute-/Repeat-Counter liefern." + ] + }, + { + "id": "WS-01-T2", + "title": "Writer-Worker einführen", + "details": [ + "Writer läuft in eigener Goroutine und besitzt alleinige Ownership über driver.Write().", + "Nur der Writer darf Write- und Tune-nahe Timinginteraktionen mit dem Treiber koordinieren.", + "Write-Dauer, Blockzeiten und Late-Events werden pro Frame gemessen." + ] + }, + { + "id": "WS-01-T3", + "title": "Supervisor-Schicht einführen", + "details": [ + "Supervisor bewertet Queue-Füllstand, Late-Rate, Fehlerhäufigkeit und entscheidet über Normal/Fault/Recovery.", + "Supervisor ist nicht nur Logik, sondern Runtime-State-Maschine." + ] + } + ], + "acceptance_criteria": [ + "Keine direkte synchrone Generate->Write-Kopplung mehr im Hauptpfad.", + "Queue-Füllstand und Write-Latenz sind live sichtbar.", + "Kurzzeitige Write-Spikes führen nicht sofort zu hörbaren Aussetzern oder unkontrolliertem Timing-Kollaps.", + "Langlauf mit 6h Testdauer zeigt keine wachsende Drift im Kontrollpfad und keine ungebremste Fehlereskalation." + ], + "affected_files": [ + "internal/app/engine.go", + "internal/output/backend.go", + "internal/platform/soapy.go" + ], + "example_interfaces": { + "frame_queue_contract": "type FrameQueue interface { Push(frame *output.CompositeFrame) error; Pop(ctx context.Context) (*output.CompositeFrame, error); FillLevel() float64; Depth() int; Capacity() int; Stats() QueueStats }", + "queue_stats_example": { + "capacity": 3, + "depth": 2, + "fillLevel": 0.6667, + "pushTimeouts": 0, + "popTimeouts": 0, + "droppedFrames": 0, + "repeatedFrames": 0, + "mutedFrames": 0 + } + } + }, + { + "id": "WS-02", + "priority": "P0", + "title": "Explizite Runtime-State-Maschine und Fault-Handling", + "why": "Aktuell existieren im Engine-State nur idle, running, stopping. Das reicht nicht für professionelles Fehlermanagement im Sendebetrieb.", + "objective": "Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständen. Jeder kritische Fehlerpfad endet in definiertem Verhalten.", + "target_state_machine": { + "states": [ + "idle", + "arming", + "prebuffering", + "running", + "degraded", + "muted", + "faulted", + "stopping" + ], + "transition_examples": [ + { + "from": "idle", + "to": "arming", + "trigger": "StartTX angefordert und Grundvalidierung erfolgreich" + }, + { + "from": "arming", + "to": "prebuffering", + "trigger": "Driver gestartet, Queue erstellt, Generator bereit" + }, + { + "from": "prebuffering", + "to": "running", + "trigger": "Queue-Minimum erreicht" + }, + { + "from": "running", + "to": "degraded", + "trigger": "Late-Rate oberhalb Schwellwert oder Queue-Füllstand wiederholt kritisch" + }, + { + "from": "degraded", + "to": "muted", + "trigger": "Writer kann keine sichere Ausgabe mehr garantieren" + }, + { + "from": "muted", + "to": "faulted", + "trigger": "Persistenter Treiberfehler oder Recovery gescheitert" + }, + { + "from": "muted", + "to": "running", + "trigger": "Queue und Treiber wieder stabil" + } + ] + }, + "implementation_tasks": [ + { + "id": "WS-02-T1", + "title": "Fault-Klassifikation definieren", + "details": [ + "Treiberfehler", + "Write-Time-Budget überschritten", + "Queue leer", + "Queue permanent überfüllt", + "Signal-Selbsttest fehlgeschlagen", + "unerlaubte Live-Konfigurationsänderung" + ] + }, + { + "id": "WS-02-T2", + "title": "Reaktionsstrategie pro Fehlerklasse definieren", + "details": [ + "Warnen ohne Zustandswechsel", + "degraded mit Countern", + "muted mit stiller Trägerstrategie oder kompletter TX-Stille", + "faulted mit manueller oder automatischer Recovery" + ] + }, + { + "id": "WS-02-T3", + "title": "Event-Log und Fault-Historie einführen", + "details": [ + "Jeder Zustandswechsel ist auditierbar.", + "Faults enthalten Ursache, Timestamp, Metriken und letzten bekannten Runtime-Kontext." + ] + } + ], + "acceptance_criteria": [ + "Jede kritische Fehlersituation führt in einen expliziten Zustand statt in implizites Weiterlaufen.", + "Der aktuelle Runtime-State ist über API und Telemetrie jederzeit sichtbar.", + "Degraded/Muted/Faulted lassen sich in Tests gezielt triggern und verifizieren." + ], + "affected_files": [ + "internal/app/engine.go", + "internal/control/control.go" + ], + "example_fault_policy": { + "late_buffer_threshold_per_60s": 10, + "queue_critical_fill_below": 0.1, + "writer_error_burst": 3, + "policy": [ + "Bei 1-2 Einzelereignissen nur Counter erhöhen.", + "Ab Burstschwelle auf degraded schalten.", + "Wenn degraded > 5s anhält oder Write wiederholt fehlschlägt -> muted.", + "Wenn muted nicht innerhalb definierter Frist stabilisiert werden kann -> faulted." + ] + } + }, + { + "id": "WS-03", + "priority": "P0", + "title": "Semantische Korrektheit und harte Config-/Runtime-Konsistenz", + "why": "Technische Perfektion scheitert oft nicht am DSP, sondern an stillen Semantikabweichungen zwischen Config, API, Live-Update und tatsächlicher Laufzeit.", + "objective": "Ein einziger, eindeutig definierter Parameterraum. Jeder Wert hat exakt eine Bedeutung und identische Constraints in Config, HTTP-API, Runtime und Telemetrie.", + "implementation_tasks": [ + { + "id": "WS-03-T1", + "title": "Parameterinventar erstellen", + "details": [ + "Alle öffentlich und intern verwendeten Parameter inventarisieren.", + "Für jeden Parameter: Typ, Einheit, Bereich, Default, Hot-Reload-Fähigkeit, Safety-Relevanz, Telemetrie-Name." + ] + }, + { + "id": "WS-03-T2", + "title": "Validation vereinheitlichen", + "details": [ + "Config.Validate(), Engine.UpdateConfig() und API-Patch-Validierung dürfen nicht divergieren.", + "Beispiel: outputDrive muss an allen Stellen denselben Bereich haben." + ] + }, + { + "id": "WS-03-T3", + "title": "AppliedConfig einführen", + "details": [ + "Neben DesiredConfig muss es eine AppliedConfig geben.", + "API-Antworten sollen nicht nur sagen, was gewünscht wurde, sondern was tatsächlich übernommen wurde." + ] + } + ], + "acceptance_criteria": [ + "Kein Parameter hat an zwei Stellen unterschiedliche Grenzwerte oder Einheiten.", + "API kann DesiredConfig und AppliedConfig getrennt zurückgeben.", + "Ungültige Hot-Updates werden deterministisch abgelehnt und nicht teilweise angewandt." + ], + "affected_files": [ + "internal/config/config.go", + "internal/app/engine.go", + "internal/control/control.go" + ], + "example_parameter_schema": { + "name": "fm.outputDrive", + "unit": "logical composite drive factor", + "type": "float64", + "range": { + "min": 0.0, + "max": 3.0 + }, + "default": 1.0, + "hot_reloadable": true, + "safety_class": "medium", + "notes": "Darf nicht mit hardware gain oder RF gain verwechselt werden." + } + }, + { + "id": "WS-04", + "priority": "P1", + "title": "Observability, Telemetrie und Diagnosefähigkeit", + "why": "Ohne harte Telemetrie bleibt jedes Timing- oder RF-Problem ratenbasiert. Pro-Level braucht Metriken, strukturierte Logs und Diagnostik-Endpunkte.", + "objective": "Vollständige Sichtbarkeit auf Runtime, Queue, Writer, Generator, RF-Selbsttests und API-Aktivität schaffen.", + "implementation_tasks": [ + { + "id": "WS-04-T1", + "title": "Strukturiertes Logging einführen", + "details": [ + "Einheitliches Logging-Backend nutzen.", + "Keine verstreuten Printf-only Pfade als primäre Diagnose.", + "Jeder Fault, State-Change und API-Eingriff wird strukturiert geloggt." + ] + }, + { + "id": "WS-04-T2", + "title": "Prometheus-kompatible Metriken einführen", + "details": [ + "Engine-Metriken", + "Writer-Metriken", + "Queue-Metriken", + "RDS-/Pilot-Selbsttest-Metriken", + "Audio-Stream-Metriken", + "Control-Plane-Metriken" + ] + }, + { + "id": "WS-04-T3", + "title": "Debug- und Profiling-Endpunkte", + "details": [ + "pprof optional aktivierbar", + "Build-Info, Git-Commit, Build-Tags, Backend, Plattform und Runtime-Version ausgeben" + ] + } + ], + "acceptance_criteria": [ + "Jeder relevante Betriebsaspekt ist per Runtime-Endpunkt oder Metrics-Endpunkt sichtbar.", + "Fehlerfälle sind anhand von Logs und Countern nachvollziehbar, ohne den Code erneut zu lesen.", + "Langlaufprobleme lassen sich zeitlich korrelieren." + ], + "example_metrics": [ + "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" + ], + "affected_files": [ + "internal/app/engine.go", + "internal/control/control.go", + "internal/platform/soapy.go", + "internal/audio/stream.go" + ] + }, + { + "id": "WS-05", + "priority": "P1", + "title": "Sichere und erwachsene Control-Plane", + "why": "Sobald TX start/stop, Frequenz, RDS-Text oder Live-Audio per HTTP steuerbar sind, ist die Control-Plane ein sicherheitsrelevanter Teil des Systems.", + "objective": "API transport- und anwendungsseitig härten, state-aware machen und auditierbar gestalten.", + "implementation_tasks": [ + { + "id": "WS-05-T1", + "title": "Auth und Deploy-Modi definieren", + "details": [ + "Mindestens Token-Auth für Remote-Betrieb.", + "Optionale mTLS-Unterstützung für geschützte Infrastrukturen.", + "Explizite Betriebsmodi: localhost-only, trusted-lan, secured-remote." + ] + }, + { + "id": "WS-05-T2", + "title": "HTTP-Server härten", + "details": [ + "ReadTimeout", + "WriteTimeout", + "IdleTimeout", + "ReadHeaderTimeout", + "Body-Size-Limits", + "Content-Type-Validierung", + "Method enforcement" + ] + }, + { + "id": "WS-05-T3", + "title": "API semantisch aufräumen", + "details": [ + "DesiredConfig vs AppliedConfig vs RuntimeState", + "Idempotente Start/Stop-Endpunkte", + "Transaktionsartige Apply-/Reject-Antworten für Patches", + "Audit-Log pro wirksamem Eingriff" + ] + } + ], + "acceptance_criteria": [ + "Kein ungeschützter Remote-TX-Betrieb im Standardmodus.", + "API liefert deterministische und vollständige Antworten.", + "Große oder falsche Requests können das System nicht unkontrolliert stressen." + ], + "affected_files": [ + "internal/control/control.go", + "cmd/fmrtx/main.go" + ], + "example_patch_response": { + "ok": true, + "requestedChangeId": "cfg-2026-04-05T12:00:00Z-0001", + "desired": { + "frequencyMHz": 100.2, + "ps": "JAN FM" + }, + "applied": { + "frequencyMHz": 100.2, + "ps": "JAN FM" + }, + "state": "running", + "warnings": [] + } + }, + { + "id": "WS-06", + "priority": "P2", + "title": "Hardware-in-the-loop und externe RF-Wahrheitsprüfung", + "why": "Ein DSP-System ist erst dann pro-tauglich, wenn es auf echter Hardware und mit externem Decoder/Messergebnis wiederholt korrekt ist.", + "objective": "Nightly- oder manuell triggerbare Hardware-Regressionen etablieren, die nicht nur intern, sondern extern prüfen, was tatsächlich gesendet wird.", + "implementation_tasks": [ + { + "id": "WS-06-T1", + "title": "Loopback-/Capture-Setup definieren", + "details": [ + "Referenz-SDR oder definierter externer Empfänger als Capture-Seite.", + "Leistungsschutz und Dummy-Load-Konzept klar dokumentieren.", + "Standard-Testfrequenz, Standard-Device-Rate und Standard-Testdauer festlegen." + ] + }, + { + "id": "WS-06-T2", + "title": "Automatisierte Analyse bauen", + "details": [ + "Pilotenergie 19 kHz", + "Stereoenergie 38 kHz", + "RDS-Energie 57 kHz", + "Deviation/Composite-Level", + "Trägergenauigkeit", + "RDS-Decodierbarkeit und Fehlerrate", + "Langlaufdrift" + ] + }, + { + "id": "WS-06-T3", + "title": "Externen Decoder in Regression einbinden", + "details": [ + "PS und RT müssen extern decodierbar und stabil sein.", + "Nicht nur intern erzeugte Bits prüfen." + ] + } + ], + "acceptance_criteria": [ + "Definierte Testsignale werden extern reproduzierbar korrekt empfangen.", + "Pilot-, Stereo- und RDS-Komponenten liegen mit stabilen Pegeln an den erwarteten Frequenzen.", + "Langlauftests zeigen keine schleichende Entwertung der Sendekette." + ], + "affected_files": [ + "internal/offline/spectral_test.go", + "internal/platform/soapy.go", + "scripts/*" + ], + "example_hil_report": { + "device": "pluto", + "testDurationMinutes": 30, + "pilot19kDetected": true, + "stereo38kDetected": true, + "rds57kDetected": true, + "rdsDecodeSuccessRate": 0.998, + "maxCarrierErrorHz": 12.0, + "maxLateBuffers": 0, + "result": "pass" + } + }, + { + "id": "WS-07", + "priority": "P2", + "title": "Device-aware Capability- und Kalibrierungsmodell", + "why": "Ein generisches Backend-Modell reicht nicht, wenn unterschiedliche SDRs unterschiedliche Raten, Gains, Latenzen und Abweichungen haben.", + "objective": "Pro Gerät bzw. Gerätetyp bekannte Fähigkeiten und Kalibrierungen explizit abbilden.", + "implementation_tasks": [ + { + "id": "WS-07-T1", + "title": "Capabilities ausbauen", + "details": [ + "Sample-Rate-Sets oder Bereiche", + "Gain-Semantik", + "Frequenzraster", + "MTU-/Buffer-Empfehlungen", + "Minimale stabile Chunkgrößen", + "Tune-Latenzverhalten" + ] + }, + { + "id": "WS-07-T2", + "title": "Kalibrierungsprofil einführen", + "details": [ + "frequencyOffsetHz", + "deviationScale", + "mpxGainCalibration", + "driverChunkRecommendation", + "safeDefaultGain" + ] + }, + { + "id": "WS-07-T3", + "title": "Device-aware Validation", + "details": [ + "Config nicht nur gegen generische Regeln validieren, sondern gegen bekannte Device-Fähigkeiten.", + "Nicht unterstützte Raten oder gefährliche Kombinationen früh blockieren." + ] + } + ], + "acceptance_criteria": [ + "Treiber und Runtime wissen explizit, was das konkrete Device sicher kann.", + "Kalibrierte Geräte liefern reproduzierbarere RF-Ergebnisse.", + "Fehlkonfigurationen werden vor TX-Beginn erkannt." + ], + "affected_files": [ + "internal/platform/soapy.go", + "internal/config/config.go" + ], + "example_calibration_profile": { + "deviceId": "pluto-serial-1234", + "sampleRateHz": 1000000, + "frequencyOffsetHz": -18.0, + "deviationScale": 0.97, + "mpxGainCalibration": 1.03, + "safeDefaultGainDb": -20.0, + "preferredChunkMs": 50 + } + }, + { + "id": "WS-08", + "priority": "P2", + "title": "Signal-Selbstüberwachung im Betrieb", + "why": "Nur Post-mortem-Messungen reichen nicht. Das System soll im Betrieb merken, wenn Pilot, RDS oder Composite auffällig werden.", + "objective": "Leichtgewichtige In-Band-Selbsttests auf Chunk-Basis oder in Intervallen ausführen und in Fault-Logik einspeisen.", + "implementation_tasks": [ + { + "id": "WS-08-T1", + "title": "Chunk-basierte RF-Checks definieren", + "details": [ + "Goertzel oder kleine FFT für 19 kHz, 38 kHz und 57 kHz", + "Composite-Clipping- und Pegelindikatoren", + "Optional Deviation-Schätzer" + ] + }, + { + "id": "WS-08-T2", + "title": "Anomalieerkennung definieren", + "details": [ + "Pilot fehlt", + "RDS ungewöhnlich schwach", + "unerwartete Composite-Energieverteilung", + "langsame Drift" + ] + } + ], + "acceptance_criteria": [ + "Runtime kann relevante RF-Anomalien erkennen und melden.", + "Selbsttests sind ausreichend billig, um die Echtzeitfähigkeit nicht zu gefährden." + ], + "affected_files": [ + "internal/dsp/goertzel.go", + "internal/offline/generator.go", + "internal/app/engine.go" + ] + }, + { + "id": "WS-09", + "priority": "P3", + "title": "Teststrategie von Unit-Tests zu echter Qualitätsabsicherung erweitern", + "why": "Viele Tests sind gut. Die richtigen Testklassen sind besser. Pro-Level verlangt deterministische Regressionen, Fuzzing und Concurrency-Tests.", + "objective": "Testpyramide so ausbauen, dass Signal, Runtime und API gleichermaßen abgesichert sind.", + "implementation_tasks": [ + { + "id": "WS-09-T1", + "title": "Golden-Vector-Tests", + "details": [ + "Definierte Inputsignale und erwartete Analysewerte festschreiben.", + "Nicht nur boolsche Pass/Fail-Aussagen, sondern tolerierte numerische Fenster." + ] + }, + { + "id": "WS-09-T2", + "title": "Long-run Regressionen", + "details": [ + "Tausende Chunks durchlaufen lassen.", + "Boundary-Continuity, Drift, Queue-Stabilität, Writer-Stabilität prüfen." + ] + }, + { + "id": "WS-09-T3", + "title": "Race Detector, Fuzzing und API-Mutation-Tests", + "details": [ + "Konfigurationspatches", + "RDS-Texte", + "Audio-Ingest", + "Start/Stop-Rennen", + "gleichzeitige Live-Updates" + ] + } + ], + "acceptance_criteria": [ + "Es gibt Regressionen für DSP, Runtime, API und HIL.", + "Race Detector und Fuzzing finden keine bekannten kritischen Pfade mehr.", + "Nightly-Regressions geben maschinenlesbare Berichte aus." + ] + }, + { + "id": "WS-10", + "priority": "P4", + "title": "Service-Reife, Packaging und Reproduzierbarkeit", + "why": "Ein System ist nicht professionell, wenn nur der Autor es zuverlässig starten kann.", + "objective": "Saubere Build-, Release- und Betriebsartefakte bereitstellen.", + "implementation_tasks": [ + { + "id": "WS-10-T1", + "title": "Reproduzierbare Builds", + "details": [ + "Build-Flags, Version, Commit und Tags ins Binary schreiben.", + "Artefakte pro Zielplattform konsistent erzeugen." + ] + }, + { + "id": "WS-10-T2", + "title": "Service-Units und Beispiel-Deployments", + "details": [ + "systemd unit", + "EnvironmentFile-Unterstützung", + "Beispielkonfigurationen pro Backend" + ] + }, + { + "id": "WS-10-T3", + "title": "Migrations- und Schema-Versionierung", + "details": [ + "Config-Versionen einführen.", + "Klare Migrationsstrategie für ältere JSON-Konfigurationen." + ] + } + ], + "acceptance_criteria": [ + "Standardisierte Start- und Betriebswege existieren.", + "Build- und Runtime-Version sind eindeutig sichtbar.", + "Alte Konfigurationen können kontrolliert migriert werden." + ] + } + ], + "implementation_sequence": [ + { + "order": 1, + "workstreams": [ + "WS-03", + "WS-01", + "WS-02" + ], + "reason": "Erst muss die Semantik stimmen, dann die deterministische Pipeline, dann das Fault-Modell. Sonst baut man saubere Infrastruktur auf falschen Annahmen." + }, + { + "order": 2, + "workstreams": [ + "WS-04", + "WS-05" + ], + "reason": "Sobald Runtime-Struktur sauber ist, müssen Sichtbarkeit und sichere Steuerung folgen. Sonst ist der neue Kern schwer überprüfbar und riskant bedienbar." + }, + { + "order": 3, + "workstreams": [ + "WS-06", + "WS-07", + "WS-08" + ], + "reason": "Jetzt wird die Hardware-Wahrheit und RF-Qualität fest verdrahtet: HIL, Kalibrierung und laufende Signalkontrolle." + }, + { + "order": 4, + "workstreams": [ + "WS-09", + "WS-10" + ], + "reason": "Zum Schluss werden Regressionstiefe, Packaging und Service-Reife maximal professionalisiert." + } + ], + "cross_cutting_rules": { + "musts": [ + "Jeder neue Runtime-Zustand muss per API und Telemetrie sichtbar sein.", + "Jede neue Recovery- oder Drop-/Mute-Strategie braucht Counter, Logs und Tests.", + "Keine neue Konfigurationsoption 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." + ] + }, + "concrete_examples": { + "example_runtime_status": { + "state": "degraded", + "substate": "writer_backpressure", + "engine": { + "chunksProduced": 128443, + "lateBuffers": 7, + "underruns": 0, + "maxCycleMs": 51.12, + "maxWriteMs": 49.91 + }, + "queue": { + "capacity": 3, + "depth": 1, + "fillLevel": 0.3333, + "droppedFrames": 0, + "repeatedFrames": 2, + "mutedFrames": 0 + }, + "driver": { + "txEnabled": true, + "streamActive": true, + "samplesWritten": 64221500, + "slowWrites": 4 + }, + "lastFault": { + "code": "WRITER-LATE-BURST", + "at": "2026-04-05T12:34:56Z", + "message": "Write-Latenz wiederholt über Chunk-Budget" + } + }, + "example_fault_event": { + "eventId": "fault-000184", + "severity": "error", + "stateBefore": "running", + "stateAfter": "muted", + "code": "QUEUE-EMPTY-RECOVERY-FAILED", + "message": "Queue blieb trotz Recovery leer; Ausgang wurde auf Mute gesetzt", + "metrics": { + "queueFillLevel": 0.0, + "lateBuffersLast60s": 14, + "writerErrorsLast60s": 3 + } + }, + "example_rollout_plan": { + "milestone_1": "Semantik und State-Maschine stabil", + "milestone_2": "Entkoppelter Writer mit Telemetrie", + "milestone_3": "Sichere API und Audit-Log", + "milestone_4": "HIL-Regression und Kalibrierung", + "milestone_5": "Nightly-Qualitätsgates und reproduzierbare Releases" + } + }, + "definition_of_done": { + "technical": [ + "TX-Pfad ist deterministisch entkoppelt und fault-tolerant.", + "DesiredConfig, AppliedConfig und RuntimeState sind sauber getrennt.", + "Alle kritischen Fehler führen in explizite Zustände.", + "RF-Signalqualität ist intern und extern nachweisbar.", + "Metriken, strukturierte Logs und Diagnosepfade sind vollständig." + ], + "operational": [ + "System läuft im Langlauftest stabil.", + "Remote-Bedienung ist gehärtet und auditierbar.", + "Gerätespezifische Fähigkeiten und Kalibrierungen sind modelliert.", + "Builds und Deployments sind reproduzierbar." + ], + "quality_gates": [ + "Unit- und Integrationssuite grün", + "Race Detector grün", + "Fuzzing ohne kritische Findings", + "HIL-Regression grün", + "Soak-Test grün" + ] + }, + "final_instruction_to_ai_team": { + "summary": "Nicht blind Features ergänzen. Zuerst die Semantik und den Runtime-Kern hart machen. Dann Observability und sichere Steuerung. Danach Hardware-Wahrheit, Kalibrierung und Nightly-Regressions. Alles, was nicht deterministisch, messbar und fault-beherrscht ist, ist noch nicht pro-level.", + "first_step_now": [ + "WS-03 beginnen: Parameterinventar und semantische Vereinheitlichung.", + "Danach direkt WS-01 und WS-02 in einem Architektur-Branch umsetzen." + ] + } +}