{ "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." ] } }