Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

832 lines
33KB

  1. {
  2. "document_type": "technical_concept",
  3. "language": "de",
  4. "audience": [
  5. "AI-Entwicklerteam",
  6. "Reviewer",
  7. "Maintainer"
  8. ],
  9. "project": {
  10. "name": "fm-rds-tx",
  11. "goal": "Aus dem bestehenden FM-Stereo/RDS-TX-System ein technisch sauberes, deterministisches, messbares und betriebsfestes Pro-Level-System machen.",
  12. "primary_priority": "Technische Perfektion",
  13. "secondary_priority": "Sinnvolle Umsetzungsreihenfolge mit maximalem Risikoabbau zuerst"
  14. },
  15. "executive_summary": {
  16. "current_strengths": [
  17. "Saubere Modultrennung zwischen Generator, DSP, Control, Audio, Backend und Plattform.",
  18. "Persistenter DSP-Zustand ist bereits vorhanden, insbesondere im Generator und im FM-Upsampler.",
  19. "Live-Updates werden bereits über atomare Snapshots bzw. Pointer modelliert.",
  20. "Es existieren bereits viele Unit-Tests sowie spektrale Blackbox-Tests für 19 kHz, 38 kHz und 57 kHz."
  21. ],
  22. "current_limitations": [
  23. "Der TX-Pfad in internal/app/engine.go ist noch ein einzelner synchroner Generate/Upsample/Write-Loop ohne entkoppelten Echtzeitpuffer.",
  24. "Die Runtime-Recovery ist schwach: bei Fehlern wird nur gezählt, geloggt und gewartet; es gibt keinen expliziten Fault-State mit deterministischem Fallback.",
  25. "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.",
  26. "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.",
  27. "Device-Abstraktion ist brauchbar, aber noch nicht streng genug capability- und kalibrierungsgetrieben.",
  28. "Observability ist noch zu schwach für echten Dauerbetrieb und reproduzierbare Fehleranalyse."
  29. ],
  30. "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."
  31. },
  32. "repo_grounding": {
  33. "confirmed_code_touchpoints": [
  34. {
  35. "path": "internal/app/engine.go",
  36. "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."
  37. },
  38. {
  39. "path": "internal/offline/generator.go",
  40. "observation": "Generator hat bereits persistenten Zustand, LiveParams per atomic.Pointer und sinnvolle DSP-Kette inklusive optionalem BS.412-Limiter."
  41. },
  42. {
  43. "path": "internal/control/control.go",
  44. "observation": "HTTP-Control existiert bereits, aber ohne sichtbare Authentisierung, ohne Server-Timeout-Konfiguration und ohne harte API-Grenzen."
  45. },
  46. {
  47. "path": "internal/config/config.go",
  48. "observation": "Validation ist vorhanden, aber semantisch nicht überall konsistent mit Laufzeitregeln."
  49. },
  50. {
  51. "path": "internal/platform/soapy.go",
  52. "observation": "Capabilities und RuntimeStats existieren als Ansatz, reichen aber noch nicht für eine wirklich harte device-aware Steuerung."
  53. },
  54. {
  55. "path": "internal/audio/stream.go",
  56. "observation": "Lock-freier SPSC-Ringbuffer für Live-Audio ist bereits vorhanden und kann als Referenz für deterministische Buffer-Designs dienen."
  57. }
  58. ],
  59. "confirmed_inconsistencies": [
  60. {
  61. "id": "CFG-SEM-001",
  62. "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."
  63. },
  64. {
  65. "id": "CTL-UX-001",
  66. "description": "handleAudioStream() nennt in der Fehlermeldung '--audio-http', im CLI ist dieser Schalter nicht als gleichwertiger offensichtlicher Bedienpfad bestätigt."
  67. }
  68. ]
  69. },
  70. "design_principles": [
  71. "Kein verstecktes Glück: Jeder relevante Echtzeit- oder RF-Pfad muss deterministisch, messbar und reproduzierbar sein.",
  72. "Fail-safe statt fail-weird: Bei Unsicherheit oder Überlast lieber definiert muten oder faulten als kaputt weiterzusenden.",
  73. "Eine Runtime-Wahrheit: Konfiguration, Live-State und tatsächlich angewandte Hardware-/DSP-Parameter dürfen nicht auseinanderlaufen.",
  74. "Hardware ist Wahrheit: IQ-Dateien und Unit-Tests reichen nicht; es braucht Hardware-in-the-loop und externe Decoder-/Messvalidierung.",
  75. "Observability ist Pflicht, nicht Luxus: Kein Pro-Level ohne Metriken, strukturierte Logs, Fault-Telemetrie und reproduzierbare Regressionen.",
  76. "Keine implizite Semantik: Alle Parameter müssen in Config, API, Runtime und Telemetrie exakt dasselbe bedeuten."
  77. ],
  78. "priority_model": {
  79. "P0": "Technische Perfektion und Determinismus",
  80. "P1": "Betriebssicherheit und Fehlerbeherrschung",
  81. "P2": "Hardware-Wahrheit und RF-Qualität",
  82. "P3": "Sichere und saubere Runtime-Steuerung",
  83. "P4": "Deployment-, Release- und Service-Reife"
  84. },
  85. "workstreams": [
  86. {
  87. "id": "WS-01",
  88. "priority": "P0",
  89. "title": "Deterministische Echtzeit-TX-Pipeline mit entkoppeltem Writer",
  90. "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.",
  91. "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.",
  92. "target_architecture": {
  93. "pipeline": [
  94. "control-plane",
  95. "runtime supervisor",
  96. "generator worker",
  97. "optional upsampler worker",
  98. "bounded frame queue",
  99. "writer worker",
  100. "driver"
  101. ],
  102. "queue_policy": {
  103. "type": "bounded ring queue",
  104. "capacity_frames": 3,
  105. "default_behavior": "Producer füllt vor, Writer sendet in Echtzeit, Supervisor überwacht Queue-Füllstand",
  106. "allowed_strategies": [
  107. "block producer kurzzeitig",
  108. "repeat last safe frame im Fault-Recovery-Modus",
  109. "mute frame im Sicherheitsmodus"
  110. ],
  111. "forbidden_strategies": [
  112. "unbounded buffering",
  113. "still und heimlich alte Frames verwerfen ohne Counter/Log",
  114. "dynamisches Verhalten ohne Telemetrie"
  115. ]
  116. }
  117. },
  118. "implementation_tasks": [
  119. {
  120. "id": "WS-01-T1",
  121. "title": "FrameQueue einführen",
  122. "details": [
  123. "Neue interne Queue-Struktur für CompositeFrame oder DeviceFrame einführen.",
  124. "Explizit festlegen, ob vor oder nach FMUpsampler gepuffert wird. Empfehlung: Puffern auf Device-Frame-Ebene, damit der Writer nur noch sendet.",
  125. "FrameQueue muss feste Kapazität, Füllstand, High-Watermark, Low-Watermark und Drop-/Mute-/Repeat-Counter liefern."
  126. ]
  127. },
  128. {
  129. "id": "WS-01-T2",
  130. "title": "Writer-Worker einführen",
  131. "details": [
  132. "Writer läuft in eigener Goroutine und besitzt alleinige Ownership über driver.Write().",
  133. "Nur der Writer darf Write- und Tune-nahe Timinginteraktionen mit dem Treiber koordinieren.",
  134. "Write-Dauer, Blockzeiten und Late-Events werden pro Frame gemessen."
  135. ]
  136. },
  137. {
  138. "id": "WS-01-T3",
  139. "title": "Supervisor-Schicht einführen",
  140. "details": [
  141. "Supervisor bewertet Queue-Füllstand, Late-Rate, Fehlerhäufigkeit und entscheidet über Normal/Fault/Recovery.",
  142. "Supervisor ist nicht nur Logik, sondern Runtime-State-Maschine."
  143. ]
  144. }
  145. ],
  146. "acceptance_criteria": [
  147. "Keine direkte synchrone Generate->Write-Kopplung mehr im Hauptpfad.",
  148. "Queue-Füllstand und Write-Latenz sind live sichtbar.",
  149. "Kurzzeitige Write-Spikes führen nicht sofort zu hörbaren Aussetzern oder unkontrolliertem Timing-Kollaps.",
  150. "Langlauf mit 6h Testdauer zeigt keine wachsende Drift im Kontrollpfad und keine ungebremste Fehlereskalation."
  151. ],
  152. "affected_files": [
  153. "internal/app/engine.go",
  154. "internal/output/backend.go",
  155. "internal/platform/soapy.go"
  156. ],
  157. "example_interfaces": {
  158. "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 }",
  159. "queue_stats_example": {
  160. "capacity": 3,
  161. "depth": 2,
  162. "fillLevel": 0.6667,
  163. "pushTimeouts": 0,
  164. "popTimeouts": 0,
  165. "droppedFrames": 0,
  166. "repeatedFrames": 0,
  167. "mutedFrames": 0
  168. }
  169. }
  170. },
  171. {
  172. "id": "WS-02",
  173. "priority": "P0",
  174. "title": "Explizite Runtime-State-Maschine und Fault-Handling",
  175. "why": "Aktuell existieren im Engine-State nur idle, running, stopping. Das reicht nicht für professionelles Fehlermanagement im Sendebetrieb.",
  176. "objective": "Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständen. Jeder kritische Fehlerpfad endet in definiertem Verhalten.",
  177. "target_state_machine": {
  178. "states": [
  179. "idle",
  180. "arming",
  181. "prebuffering",
  182. "running",
  183. "degraded",
  184. "muted",
  185. "faulted",
  186. "stopping"
  187. ],
  188. "transition_examples": [
  189. {
  190. "from": "idle",
  191. "to": "arming",
  192. "trigger": "StartTX angefordert und Grundvalidierung erfolgreich"
  193. },
  194. {
  195. "from": "arming",
  196. "to": "prebuffering",
  197. "trigger": "Driver gestartet, Queue erstellt, Generator bereit"
  198. },
  199. {
  200. "from": "prebuffering",
  201. "to": "running",
  202. "trigger": "Queue-Minimum erreicht"
  203. },
  204. {
  205. "from": "running",
  206. "to": "degraded",
  207. "trigger": "Late-Rate oberhalb Schwellwert oder Queue-Füllstand wiederholt kritisch"
  208. },
  209. {
  210. "from": "degraded",
  211. "to": "muted",
  212. "trigger": "Writer kann keine sichere Ausgabe mehr garantieren"
  213. },
  214. {
  215. "from": "muted",
  216. "to": "faulted",
  217. "trigger": "Persistenter Treiberfehler oder Recovery gescheitert"
  218. },
  219. {
  220. "from": "muted",
  221. "to": "running",
  222. "trigger": "Queue und Treiber wieder stabil"
  223. }
  224. ]
  225. },
  226. "implementation_tasks": [
  227. {
  228. "id": "WS-02-T1",
  229. "title": "Fault-Klassifikation definieren",
  230. "details": [
  231. "Treiberfehler",
  232. "Write-Time-Budget überschritten",
  233. "Queue leer",
  234. "Queue permanent überfüllt",
  235. "Signal-Selbsttest fehlgeschlagen",
  236. "unerlaubte Live-Konfigurationsänderung"
  237. ]
  238. },
  239. {
  240. "id": "WS-02-T2",
  241. "title": "Reaktionsstrategie pro Fehlerklasse definieren",
  242. "details": [
  243. "Warnen ohne Zustandswechsel",
  244. "degraded mit Countern",
  245. "muted mit stiller Trägerstrategie oder kompletter TX-Stille",
  246. "faulted mit manueller oder automatischer Recovery"
  247. ]
  248. },
  249. {
  250. "id": "WS-02-T3",
  251. "title": "Event-Log und Fault-Historie einführen",
  252. "details": [
  253. "Jeder Zustandswechsel ist auditierbar.",
  254. "Faults enthalten Ursache, Timestamp, Metriken und letzten bekannten Runtime-Kontext."
  255. ]
  256. }
  257. ],
  258. "acceptance_criteria": [
  259. "Jede kritische Fehlersituation führt in einen expliziten Zustand statt in implizites Weiterlaufen.",
  260. "Der aktuelle Runtime-State ist über API und Telemetrie jederzeit sichtbar.",
  261. "Degraded/Muted/Faulted lassen sich in Tests gezielt triggern und verifizieren."
  262. ],
  263. "affected_files": [
  264. "internal/app/engine.go",
  265. "internal/control/control.go"
  266. ],
  267. "example_fault_policy": {
  268. "late_buffer_threshold_per_60s": 10,
  269. "queue_critical_fill_below": 0.1,
  270. "writer_error_burst": 3,
  271. "policy": [
  272. "Bei 1-2 Einzelereignissen nur Counter erhöhen.",
  273. "Ab Burstschwelle auf degraded schalten.",
  274. "Wenn degraded > 5s anhält oder Write wiederholt fehlschlägt -> muted.",
  275. "Wenn muted nicht innerhalb definierter Frist stabilisiert werden kann -> faulted."
  276. ]
  277. }
  278. },
  279. {
  280. "id": "WS-03",
  281. "priority": "P0",
  282. "title": "Semantische Korrektheit und harte Config-/Runtime-Konsistenz",
  283. "why": "Technische Perfektion scheitert oft nicht am DSP, sondern an stillen Semantikabweichungen zwischen Config, API, Live-Update und tatsächlicher Laufzeit.",
  284. "objective": "Ein einziger, eindeutig definierter Parameterraum. Jeder Wert hat exakt eine Bedeutung und identische Constraints in Config, HTTP-API, Runtime und Telemetrie.",
  285. "implementation_tasks": [
  286. {
  287. "id": "WS-03-T1",
  288. "title": "Parameterinventar erstellen",
  289. "details": [
  290. "Alle öffentlich und intern verwendeten Parameter inventarisieren.",
  291. "Für jeden Parameter: Typ, Einheit, Bereich, Default, Hot-Reload-Fähigkeit, Safety-Relevanz, Telemetrie-Name."
  292. ]
  293. },
  294. {
  295. "id": "WS-03-T2",
  296. "title": "Validation vereinheitlichen",
  297. "details": [
  298. "Config.Validate(), Engine.UpdateConfig() und API-Patch-Validierung dürfen nicht divergieren.",
  299. "Beispiel: outputDrive muss an allen Stellen denselben Bereich haben."
  300. ]
  301. },
  302. {
  303. "id": "WS-03-T3",
  304. "title": "AppliedConfig einführen",
  305. "details": [
  306. "Neben DesiredConfig muss es eine AppliedConfig geben.",
  307. "API-Antworten sollen nicht nur sagen, was gewünscht wurde, sondern was tatsächlich übernommen wurde."
  308. ]
  309. }
  310. ],
  311. "acceptance_criteria": [
  312. "Kein Parameter hat an zwei Stellen unterschiedliche Grenzwerte oder Einheiten.",
  313. "API kann DesiredConfig und AppliedConfig getrennt zurückgeben.",
  314. "Ungültige Hot-Updates werden deterministisch abgelehnt und nicht teilweise angewandt."
  315. ],
  316. "affected_files": [
  317. "internal/config/config.go",
  318. "internal/app/engine.go",
  319. "internal/control/control.go"
  320. ],
  321. "example_parameter_schema": {
  322. "name": "fm.outputDrive",
  323. "unit": "logical composite drive factor",
  324. "type": "float64",
  325. "range": {
  326. "min": 0.0,
  327. "max": 3.0
  328. },
  329. "default": 1.0,
  330. "hot_reloadable": true,
  331. "safety_class": "medium",
  332. "notes": "Darf nicht mit hardware gain oder RF gain verwechselt werden."
  333. }
  334. },
  335. {
  336. "id": "WS-04",
  337. "priority": "P1",
  338. "title": "Observability, Telemetrie und Diagnosefähigkeit",
  339. "why": "Ohne harte Telemetrie bleibt jedes Timing- oder RF-Problem ratenbasiert. Pro-Level braucht Metriken, strukturierte Logs und Diagnostik-Endpunkte.",
  340. "objective": "Vollständige Sichtbarkeit auf Runtime, Queue, Writer, Generator, RF-Selbsttests und API-Aktivität schaffen.",
  341. "implementation_tasks": [
  342. {
  343. "id": "WS-04-T1",
  344. "title": "Strukturiertes Logging einführen",
  345. "details": [
  346. "Einheitliches Logging-Backend nutzen.",
  347. "Keine verstreuten Printf-only Pfade als primäre Diagnose.",
  348. "Jeder Fault, State-Change und API-Eingriff wird strukturiert geloggt."
  349. ]
  350. },
  351. {
  352. "id": "WS-04-T2",
  353. "title": "Prometheus-kompatible Metriken einführen",
  354. "details": [
  355. "Engine-Metriken",
  356. "Writer-Metriken",
  357. "Queue-Metriken",
  358. "RDS-/Pilot-Selbsttest-Metriken",
  359. "Audio-Stream-Metriken",
  360. "Control-Plane-Metriken"
  361. ]
  362. },
  363. {
  364. "id": "WS-04-T3",
  365. "title": "Debug- und Profiling-Endpunkte",
  366. "details": [
  367. "pprof optional aktivierbar",
  368. "Build-Info, Git-Commit, Build-Tags, Backend, Plattform und Runtime-Version ausgeben"
  369. ]
  370. }
  371. ],
  372. "acceptance_criteria": [
  373. "Jeder relevante Betriebsaspekt ist per Runtime-Endpunkt oder Metrics-Endpunkt sichtbar.",
  374. "Fehlerfälle sind anhand von Logs und Countern nachvollziehbar, ohne den Code erneut zu lesen.",
  375. "Langlaufprobleme lassen sich zeitlich korrelieren."
  376. ],
  377. "example_metrics": [
  378. "engine_chunks_generated_total",
  379. "engine_late_buffers_total",
  380. "engine_fault_transitions_total",
  381. "writer_write_duration_seconds",
  382. "queue_fill_ratio",
  383. "queue_dropped_frames_total",
  384. "queue_muted_frames_total",
  385. "driver_write_errors_total",
  386. "audio_stream_underruns_total",
  387. "audio_stream_overflows_total",
  388. "rf_selftest_pilot_db",
  389. "rf_selftest_rds_57k_db"
  390. ],
  391. "affected_files": [
  392. "internal/app/engine.go",
  393. "internal/control/control.go",
  394. "internal/platform/soapy.go",
  395. "internal/audio/stream.go"
  396. ]
  397. },
  398. {
  399. "id": "WS-05",
  400. "priority": "P1",
  401. "title": "Sichere und erwachsene Control-Plane",
  402. "why": "Sobald TX start/stop, Frequenz, RDS-Text oder Live-Audio per HTTP steuerbar sind, ist die Control-Plane ein sicherheitsrelevanter Teil des Systems.",
  403. "objective": "API transport- und anwendungsseitig härten, state-aware machen und auditierbar gestalten.",
  404. "implementation_tasks": [
  405. {
  406. "id": "WS-05-T1",
  407. "title": "Auth und Deploy-Modi definieren",
  408. "details": [
  409. "Mindestens Token-Auth für Remote-Betrieb.",
  410. "Optionale mTLS-Unterstützung für geschützte Infrastrukturen.",
  411. "Explizite Betriebsmodi: localhost-only, trusted-lan, secured-remote."
  412. ]
  413. },
  414. {
  415. "id": "WS-05-T2",
  416. "title": "HTTP-Server härten",
  417. "details": [
  418. "ReadTimeout",
  419. "WriteTimeout",
  420. "IdleTimeout",
  421. "ReadHeaderTimeout",
  422. "Body-Size-Limits",
  423. "Content-Type-Validierung",
  424. "Method enforcement"
  425. ]
  426. },
  427. {
  428. "id": "WS-05-T3",
  429. "title": "API semantisch aufräumen",
  430. "details": [
  431. "DesiredConfig vs AppliedConfig vs RuntimeState",
  432. "Idempotente Start/Stop-Endpunkte",
  433. "Transaktionsartige Apply-/Reject-Antworten für Patches",
  434. "Audit-Log pro wirksamem Eingriff"
  435. ]
  436. }
  437. ],
  438. "acceptance_criteria": [
  439. "Kein ungeschützter Remote-TX-Betrieb im Standardmodus.",
  440. "API liefert deterministische und vollständige Antworten.",
  441. "Große oder falsche Requests können das System nicht unkontrolliert stressen."
  442. ],
  443. "affected_files": [
  444. "internal/control/control.go",
  445. "cmd/fmrtx/main.go"
  446. ],
  447. "example_patch_response": {
  448. "ok": true,
  449. "requestedChangeId": "cfg-2026-04-05T12:00:00Z-0001",
  450. "desired": {
  451. "frequencyMHz": 100.2,
  452. "ps": "JAN FM"
  453. },
  454. "applied": {
  455. "frequencyMHz": 100.2,
  456. "ps": "JAN FM"
  457. },
  458. "state": "running",
  459. "warnings": []
  460. }
  461. },
  462. {
  463. "id": "WS-06",
  464. "priority": "P2",
  465. "title": "Hardware-in-the-loop und externe RF-Wahrheitsprüfung",
  466. "why": "Ein DSP-System ist erst dann pro-tauglich, wenn es auf echter Hardware und mit externem Decoder/Messergebnis wiederholt korrekt ist.",
  467. "objective": "Nightly- oder manuell triggerbare Hardware-Regressionen etablieren, die nicht nur intern, sondern extern prüfen, was tatsächlich gesendet wird.",
  468. "implementation_tasks": [
  469. {
  470. "id": "WS-06-T1",
  471. "title": "Loopback-/Capture-Setup definieren",
  472. "details": [
  473. "Referenz-SDR oder definierter externer Empfänger als Capture-Seite.",
  474. "Leistungsschutz und Dummy-Load-Konzept klar dokumentieren.",
  475. "Standard-Testfrequenz, Standard-Device-Rate und Standard-Testdauer festlegen."
  476. ]
  477. },
  478. {
  479. "id": "WS-06-T2",
  480. "title": "Automatisierte Analyse bauen",
  481. "details": [
  482. "Pilotenergie 19 kHz",
  483. "Stereoenergie 38 kHz",
  484. "RDS-Energie 57 kHz",
  485. "Deviation/Composite-Level",
  486. "Trägergenauigkeit",
  487. "RDS-Decodierbarkeit und Fehlerrate",
  488. "Langlaufdrift"
  489. ]
  490. },
  491. {
  492. "id": "WS-06-T3",
  493. "title": "Externen Decoder in Regression einbinden",
  494. "details": [
  495. "PS und RT müssen extern decodierbar und stabil sein.",
  496. "Nicht nur intern erzeugte Bits prüfen."
  497. ]
  498. }
  499. ],
  500. "acceptance_criteria": [
  501. "Definierte Testsignale werden extern reproduzierbar korrekt empfangen.",
  502. "Pilot-, Stereo- und RDS-Komponenten liegen mit stabilen Pegeln an den erwarteten Frequenzen.",
  503. "Langlauftests zeigen keine schleichende Entwertung der Sendekette."
  504. ],
  505. "affected_files": [
  506. "internal/offline/spectral_test.go",
  507. "internal/platform/soapy.go",
  508. "scripts/*"
  509. ],
  510. "example_hil_report": {
  511. "device": "pluto",
  512. "testDurationMinutes": 30,
  513. "pilot19kDetected": true,
  514. "stereo38kDetected": true,
  515. "rds57kDetected": true,
  516. "rdsDecodeSuccessRate": 0.998,
  517. "maxCarrierErrorHz": 12.0,
  518. "maxLateBuffers": 0,
  519. "result": "pass"
  520. }
  521. },
  522. {
  523. "id": "WS-07",
  524. "priority": "P2",
  525. "title": "Device-aware Capability- und Kalibrierungsmodell",
  526. "why": "Ein generisches Backend-Modell reicht nicht, wenn unterschiedliche SDRs unterschiedliche Raten, Gains, Latenzen und Abweichungen haben.",
  527. "objective": "Pro Gerät bzw. Gerätetyp bekannte Fähigkeiten und Kalibrierungen explizit abbilden.",
  528. "implementation_tasks": [
  529. {
  530. "id": "WS-07-T1",
  531. "title": "Capabilities ausbauen",
  532. "details": [
  533. "Sample-Rate-Sets oder Bereiche",
  534. "Gain-Semantik",
  535. "Frequenzraster",
  536. "MTU-/Buffer-Empfehlungen",
  537. "Minimale stabile Chunkgrößen",
  538. "Tune-Latenzverhalten"
  539. ]
  540. },
  541. {
  542. "id": "WS-07-T2",
  543. "title": "Kalibrierungsprofil einführen",
  544. "details": [
  545. "frequencyOffsetHz",
  546. "deviationScale",
  547. "mpxGainCalibration",
  548. "driverChunkRecommendation",
  549. "safeDefaultGain"
  550. ]
  551. },
  552. {
  553. "id": "WS-07-T3",
  554. "title": "Device-aware Validation",
  555. "details": [
  556. "Config nicht nur gegen generische Regeln validieren, sondern gegen bekannte Device-Fähigkeiten.",
  557. "Nicht unterstützte Raten oder gefährliche Kombinationen früh blockieren."
  558. ]
  559. }
  560. ],
  561. "acceptance_criteria": [
  562. "Treiber und Runtime wissen explizit, was das konkrete Device sicher kann.",
  563. "Kalibrierte Geräte liefern reproduzierbarere RF-Ergebnisse.",
  564. "Fehlkonfigurationen werden vor TX-Beginn erkannt."
  565. ],
  566. "affected_files": [
  567. "internal/platform/soapy.go",
  568. "internal/config/config.go"
  569. ],
  570. "example_calibration_profile": {
  571. "deviceId": "pluto-serial-1234",
  572. "sampleRateHz": 1000000,
  573. "frequencyOffsetHz": -18.0,
  574. "deviationScale": 0.97,
  575. "mpxGainCalibration": 1.03,
  576. "safeDefaultGainDb": -20.0,
  577. "preferredChunkMs": 50
  578. }
  579. },
  580. {
  581. "id": "WS-08",
  582. "priority": "P2",
  583. "title": "Signal-Selbstüberwachung im Betrieb",
  584. "why": "Nur Post-mortem-Messungen reichen nicht. Das System soll im Betrieb merken, wenn Pilot, RDS oder Composite auffällig werden.",
  585. "objective": "Leichtgewichtige In-Band-Selbsttests auf Chunk-Basis oder in Intervallen ausführen und in Fault-Logik einspeisen.",
  586. "implementation_tasks": [
  587. {
  588. "id": "WS-08-T1",
  589. "title": "Chunk-basierte RF-Checks definieren",
  590. "details": [
  591. "Goertzel oder kleine FFT für 19 kHz, 38 kHz und 57 kHz",
  592. "Composite-Clipping- und Pegelindikatoren",
  593. "Optional Deviation-Schätzer"
  594. ]
  595. },
  596. {
  597. "id": "WS-08-T2",
  598. "title": "Anomalieerkennung definieren",
  599. "details": [
  600. "Pilot fehlt",
  601. "RDS ungewöhnlich schwach",
  602. "unerwartete Composite-Energieverteilung",
  603. "langsame Drift"
  604. ]
  605. }
  606. ],
  607. "acceptance_criteria": [
  608. "Runtime kann relevante RF-Anomalien erkennen und melden.",
  609. "Selbsttests sind ausreichend billig, um die Echtzeitfähigkeit nicht zu gefährden."
  610. ],
  611. "affected_files": [
  612. "internal/dsp/goertzel.go",
  613. "internal/offline/generator.go",
  614. "internal/app/engine.go"
  615. ]
  616. },
  617. {
  618. "id": "WS-09",
  619. "priority": "P3",
  620. "title": "Teststrategie von Unit-Tests zu echter Qualitätsabsicherung erweitern",
  621. "why": "Viele Tests sind gut. Die richtigen Testklassen sind besser. Pro-Level verlangt deterministische Regressionen, Fuzzing und Concurrency-Tests.",
  622. "objective": "Testpyramide so ausbauen, dass Signal, Runtime und API gleichermaßen abgesichert sind.",
  623. "implementation_tasks": [
  624. {
  625. "id": "WS-09-T1",
  626. "title": "Golden-Vector-Tests",
  627. "details": [
  628. "Definierte Inputsignale und erwartete Analysewerte festschreiben.",
  629. "Nicht nur boolsche Pass/Fail-Aussagen, sondern tolerierte numerische Fenster."
  630. ]
  631. },
  632. {
  633. "id": "WS-09-T2",
  634. "title": "Long-run Regressionen",
  635. "details": [
  636. "Tausende Chunks durchlaufen lassen.",
  637. "Boundary-Continuity, Drift, Queue-Stabilität, Writer-Stabilität prüfen."
  638. ]
  639. },
  640. {
  641. "id": "WS-09-T3",
  642. "title": "Race Detector, Fuzzing und API-Mutation-Tests",
  643. "details": [
  644. "Konfigurationspatches",
  645. "RDS-Texte",
  646. "Audio-Ingest",
  647. "Start/Stop-Rennen",
  648. "gleichzeitige Live-Updates"
  649. ]
  650. }
  651. ],
  652. "acceptance_criteria": [
  653. "Es gibt Regressionen für DSP, Runtime, API und HIL.",
  654. "Race Detector und Fuzzing finden keine bekannten kritischen Pfade mehr.",
  655. "Nightly-Regressions geben maschinenlesbare Berichte aus."
  656. ]
  657. },
  658. {
  659. "id": "WS-10",
  660. "priority": "P4",
  661. "title": "Service-Reife, Packaging und Reproduzierbarkeit",
  662. "why": "Ein System ist nicht professionell, wenn nur der Autor es zuverlässig starten kann.",
  663. "objective": "Saubere Build-, Release- und Betriebsartefakte bereitstellen.",
  664. "implementation_tasks": [
  665. {
  666. "id": "WS-10-T1",
  667. "title": "Reproduzierbare Builds",
  668. "details": [
  669. "Build-Flags, Version, Commit und Tags ins Binary schreiben.",
  670. "Artefakte pro Zielplattform konsistent erzeugen."
  671. ]
  672. },
  673. {
  674. "id": "WS-10-T2",
  675. "title": "Service-Units und Beispiel-Deployments",
  676. "details": [
  677. "systemd unit",
  678. "EnvironmentFile-Unterstützung",
  679. "Beispielkonfigurationen pro Backend"
  680. ]
  681. },
  682. {
  683. "id": "WS-10-T3",
  684. "title": "Migrations- und Schema-Versionierung",
  685. "details": [
  686. "Config-Versionen einführen.",
  687. "Klare Migrationsstrategie für ältere JSON-Konfigurationen."
  688. ]
  689. }
  690. ],
  691. "acceptance_criteria": [
  692. "Standardisierte Start- und Betriebswege existieren.",
  693. "Build- und Runtime-Version sind eindeutig sichtbar.",
  694. "Alte Konfigurationen können kontrolliert migriert werden."
  695. ]
  696. }
  697. ],
  698. "implementation_sequence": [
  699. {
  700. "order": 1,
  701. "workstreams": [
  702. "WS-03",
  703. "WS-01",
  704. "WS-02"
  705. ],
  706. "reason": "Erst muss die Semantik stimmen, dann die deterministische Pipeline, dann das Fault-Modell. Sonst baut man saubere Infrastruktur auf falschen Annahmen."
  707. },
  708. {
  709. "order": 2,
  710. "workstreams": [
  711. "WS-04",
  712. "WS-05"
  713. ],
  714. "reason": "Sobald Runtime-Struktur sauber ist, müssen Sichtbarkeit und sichere Steuerung folgen. Sonst ist der neue Kern schwer überprüfbar und riskant bedienbar."
  715. },
  716. {
  717. "order": 3,
  718. "workstreams": [
  719. "WS-06",
  720. "WS-07",
  721. "WS-08"
  722. ],
  723. "reason": "Jetzt wird die Hardware-Wahrheit und RF-Qualität fest verdrahtet: HIL, Kalibrierung und laufende Signalkontrolle."
  724. },
  725. {
  726. "order": 4,
  727. "workstreams": [
  728. "WS-09",
  729. "WS-10"
  730. ],
  731. "reason": "Zum Schluss werden Regressionstiefe, Packaging und Service-Reife maximal professionalisiert."
  732. }
  733. ],
  734. "cross_cutting_rules": {
  735. "musts": [
  736. "Jeder neue Runtime-Zustand muss per API und Telemetrie sichtbar sein.",
  737. "Jede neue Recovery- oder Drop-/Mute-Strategie braucht Counter, Logs und Tests.",
  738. "Keine neue Konfigurationsoption ohne klaren Typ, Bereich, Einheit, Default und Hot-Reload-Klassifikation.",
  739. "Hardware-nahe Änderungen brauchen mindestens Simulations- und HIL-Validierung.",
  740. "Alle Faults müssen eine maschinenlesbare Ursache und eine menschenlesbare Zusammenfassung haben."
  741. ],
  742. "must_not": [
  743. "Keine unbounded Queues.",
  744. "Keine stillen Fallbacks ohne Telemetrie.",
  745. "Keine teilweise angewandten Live-Config-Änderungen ohne explizite Rückmeldung.",
  746. "Keine unterschiedlichen Grenzwerte zwischen Config, API und Runtime.",
  747. "Keine sicherheitsrelevanten HTTP-Endpunkte ohne Härtung im Remote-Betrieb."
  748. ]
  749. },
  750. "concrete_examples": {
  751. "example_runtime_status": {
  752. "state": "degraded",
  753. "substate": "writer_backpressure",
  754. "engine": {
  755. "chunksProduced": 128443,
  756. "lateBuffers": 7,
  757. "underruns": 0,
  758. "maxCycleMs": 51.12,
  759. "maxWriteMs": 49.91
  760. },
  761. "queue": {
  762. "capacity": 3,
  763. "depth": 1,
  764. "fillLevel": 0.3333,
  765. "droppedFrames": 0,
  766. "repeatedFrames": 2,
  767. "mutedFrames": 0
  768. },
  769. "driver": {
  770. "txEnabled": true,
  771. "streamActive": true,
  772. "samplesWritten": 64221500,
  773. "slowWrites": 4
  774. },
  775. "lastFault": {
  776. "code": "WRITER-LATE-BURST",
  777. "at": "2026-04-05T12:34:56Z",
  778. "message": "Write-Latenz wiederholt über Chunk-Budget"
  779. }
  780. },
  781. "example_fault_event": {
  782. "eventId": "fault-000184",
  783. "severity": "error",
  784. "stateBefore": "running",
  785. "stateAfter": "muted",
  786. "code": "QUEUE-EMPTY-RECOVERY-FAILED",
  787. "message": "Queue blieb trotz Recovery leer; Ausgang wurde auf Mute gesetzt",
  788. "metrics": {
  789. "queueFillLevel": 0.0,
  790. "lateBuffersLast60s": 14,
  791. "writerErrorsLast60s": 3
  792. }
  793. },
  794. "example_rollout_plan": {
  795. "milestone_1": "Semantik und State-Maschine stabil",
  796. "milestone_2": "Entkoppelter Writer mit Telemetrie",
  797. "milestone_3": "Sichere API und Audit-Log",
  798. "milestone_4": "HIL-Regression und Kalibrierung",
  799. "milestone_5": "Nightly-Qualitätsgates und reproduzierbare Releases"
  800. }
  801. },
  802. "definition_of_done": {
  803. "technical": [
  804. "TX-Pfad ist deterministisch entkoppelt und fault-tolerant.",
  805. "DesiredConfig, AppliedConfig und RuntimeState sind sauber getrennt.",
  806. "Alle kritischen Fehler führen in explizite Zustände.",
  807. "RF-Signalqualität ist intern und extern nachweisbar.",
  808. "Metriken, strukturierte Logs und Diagnosepfade sind vollständig."
  809. ],
  810. "operational": [
  811. "System läuft im Langlauftest stabil.",
  812. "Remote-Bedienung ist gehärtet und auditierbar.",
  813. "Gerätespezifische Fähigkeiten und Kalibrierungen sind modelliert.",
  814. "Builds und Deployments sind reproduzierbar."
  815. ],
  816. "quality_gates": [
  817. "Unit- und Integrationssuite grün",
  818. "Race Detector grün",
  819. "Fuzzing ohne kritische Findings",
  820. "HIL-Regression grün",
  821. "Soak-Test grün"
  822. ]
  823. },
  824. "final_instruction_to_ai_team": {
  825. "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.",
  826. "first_step_now": [
  827. "WS-03 beginnen: Parameterinventar und semantische Vereinheitlichung.",
  828. "Danach direkt WS-01 und WS-02 in einem Architektur-Branch umsetzen."
  829. ]
  830. }
  831. }