prd: Rev. 2 nach adversarialer 4-Linsen-Validierung (28 Findings eingearbeitet)
- BMAD v6.10.0 installiert (bmm, de), {output_folder}-Bug gefixt
- PRD + technisches Addendum (verbindliche Regel-/Degradations-/Mapping-Tabellen)
- Outfit-Komposition (Basis + Schichtsumme), Zieldatum-Logik, Min-HA >= 2025.3,
HACS-konforme Karten-/Blueprint-Auslieferung, Datenschutz/Injection-FRs,
messbare Metriken inkl. Retention, Foto -> v1.1 (E-4)
- 4 Detail-Reviews unter docs/reviews/prd/, Urteile im LEDGER (28 uebernommen, 3 verworfen mit Evidenz)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4d7ef85406
commit
a6d5635236
256 changed files with 31788 additions and 1 deletions
157
docs/reviews/prd/review-adversarial.md
Normal file
157
docs/reviews/prd/review-adversarial.md
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# Adversarial-Review — PRD „What to Wear" v1.0 (Linse: riskante Annahmen)
|
||||
|
||||
- **Reviewer-Linse:** Zynischer PM — unausgesprochene Annahmen, Schönwetter-Journeys, Feigenblatt-Metriken
|
||||
- **Geprüft:** `_bmad-output/planning-artifacts/prd.md`, `_bmad-output/planning-artifacts/prd-addendum.md`, `docs/superpowers/specs/2026-07-11-what-to-wear-design.md`
|
||||
- **Datum:** 2026-07-11
|
||||
- **Nicht bewertet (Benutzer-Entscheidungen):** Open-Source/kostenlos, Inventar echter Stücke, LLM-BYO-Key, HA-Wetter-Entität als v1-Quelle, v1.1-Verschiebungen, de+en, keine Telemetrie.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Der PRD ist handwerklich überdurchschnittlich (Invarianten, Missingness, Fehlerpfade, Teststrategie sind ernsthaft durchdacht). Aber er hat drei Löcher, die das Produkt in der realen Nutzung versenken können, bevor irgendjemand über Code redet:
|
||||
|
||||
1. **Der Lebenszyklus der Empfehlung ist nicht definiert** — die Kern-Journey J3 („Morgenblick") widerspricht der eigenen Morgen-Definition (FR-3.2/INV-2).
|
||||
2. **Die Regel-Engine kann das versprochene Ergebnis gar nicht produzieren** — die Journeys zeigen komplette Outfits, die Regeln erzeugen an den meisten Tagen ein einziges Requirement.
|
||||
3. **Die Distribution hängt an einer „offenen Kleinentscheidung"** — HACS, Default-Store, Stars und Blueprint-Import setzen öffentliches GitHub voraus; E-3 lässt genau das offen, während der Haus-Standard Forgejo ist.
|
||||
|
||||
Dazu kommen ein faktisch falscher Min-Versions-Anker, eine Datenverfügbarkeits-Annahme, die den „degradierten" Pfad zum Normalfall macht, und ein 15-Minuten-Versprechen, das mit der nativen Sub-Entry-UI sportlich bis unehrlich ist.
|
||||
|
||||
---
|
||||
|
||||
## KRITISCH
|
||||
|
||||
### K1 — „Morgen" kippt um Mitternacht: J3 widerspricht FR-3.2/INV-2; die Empfehlung hat keinen definierten Lebenszyklus
|
||||
|
||||
**Bezug:** J3 (PRD §3), FR-3.2, FR-7.5, INV-2 (Addendum §2), Spec §5/§12.
|
||||
|
||||
FR-3.2: *„»Morgen« ist das nächste Kalenderdatum in der HA-Zeitzone."* FR-7.5: Empfehlung wird *„mind. stündlich"* aktualisiert. J3 verspricht: *„Dashboard-Karte zeigt die Empfehlung weiterhin; optional fragt der Nutzer den Lautsprecher … und bekommt dieselbe Empfehlung angesagt."*
|
||||
|
||||
Das geht nicht zusammen. Um 07:00 morgens ist „das nächste Kalenderdatum" **übermorgen**. Der Nutzer, der morgens auf die Karte schaut oder den TTS-Blueprint triggert, bekommt nach der Spec-Logik die Empfehlung für den **falschen Tag** — oder die Karte zeigt eine seit Mitternacht x-fach neu berechnete Empfehlung für den Folgetag, während seine rausgelegten Sachen für heute auf dem Stuhl liegen. „Dieselbe Empfehlung" (J3) ist unter FR-3.2 + FR-7.5 schlicht falsch.
|
||||
|
||||
Zweiter Aspekt desselben Lochs: Auch **vor** Mitternacht driftet die Empfehlung. Push um 20:00 sagt „Wollpullover, Regenjacke"; die Prognose ändert sich um 23:00; die Karte zeigt morgens etwas anderes als der Push gestern Abend. Kein FR definiert, ob die Empfehlung nach dem Abend-Push **gepinnt** wird, bis wann eine Empfehlung „für morgen" morgens noch als „für heute" gilt (Cutover-Zeit), und was die Karte zwischen 00:00 und ~10:00 anzeigen soll.
|
||||
|
||||
Das ist kein Edge-Case, das ist der Kern-Use-Case (Abend rauslegen → Morgen prüfen). Ohne definierte Semantik „Empfehlung gilt für Zieldatum X, erstellt um Y, eingefroren/aktualisiert nach Regel Z" ist J3 eine Schönwetter-Journey und der erste Bug-Report ist vorprogrammiert („Karte zeigt morgens was anderes als der Push gestern").
|
||||
|
||||
**Fix:** Empfehlungs-Objekt bekommt ein explizites Zieldatum + Erstellungszeit; Cutover-Regel definieren (z. B. bis 10:00 lokal gilt das heutige Zieldatum weiter); festlegen, ob/wann nach dem Abend-Push noch aktualisiert wird (z. B. Aktualisierung ja, aber Karte kennzeichnet „geändert seit Push"). J3 und FR-3.2/FR-7.5 konsistent machen.
|
||||
|
||||
### K2 — Die Regel-Engine produziert kein Outfit; J2 verspricht eines
|
||||
|
||||
**Bezug:** FR-4.1, FR-5.1, Addendum §3 (Regel-Defaults) + §4 (Matcher), J2 (PRD §3), Spec §7/§8.
|
||||
|
||||
J2 verspricht: *„leg raus: Regenjacke, Wollpullover (Wärme 4), Jeans, feste Schuhe."* Die Regel-Tabelle (Addendum §3) erzeugt aber:
|
||||
|
||||
- Bei 15–22 °C, trocken, windstill: **genau ein Requirement** (`wärme_min:2`). Kein Requirement erzeugt jemals „Hose", „Oberteil" oder „Schuhe" (außer `feste_schuhe` bei Regen). Die Empfehlung an einem milden, trockenen Tag ist nach dieser Spec: **ein einzelnes Kleidungsstück**. Die Jeans aus J2 kommt aus keiner Regel.
|
||||
- Umgekehrt fehlt die Zuordnung Requirement → Kategorie: `wärme_min:4` — erfüllt das ein Pullover, eine Jacke, oder beide (Layering)? FR-5.1 sagt *„je Requirement … das am besten passende aktive Stück"* — also ein Stück pro Requirement. Damit kann dasselbe Stück mehrere Requirements „gewinnen", ein Winter-Requirement kann von einer dicken Hose „erfüllt" werden, und ein Schichten-Konzept (Basisschicht + Wärmeschicht + Außenschicht) existiert nicht.
|
||||
|
||||
Das ist die Sorte Lücke, die im Beta-Test sofort auffliegt: erste Empfehlung an einem 18-°C-Tag = „Zieh ein T-Shirt an" (sonst nichts). Der Nutzer lacht einmal, deinstalliert, und schreibt einen Forum-Post. Der PRD hat FR-Präzision bei Missingness und DST — aber das eigentliche Produkt (ein plausibles, vollständiges Outfit Kopf-bis-Fuß) ist nirgends als Anforderung formuliert.
|
||||
|
||||
**Fix:** FR ergänzen: Die Empfehlung deckt immer einen definierten Basis-Satz an Kategorien ab (Oberteil, Hose, Schuhe; + Jacke/Accessoires nach Wetter), Requirements werden auf Kategorien gemappt, Layering-Minimalmodell (Außenschicht vs. Wärmeschicht) definieren. Regel-Defaults in §3 entsprechend erweitern.
|
||||
|
||||
### K3 — Distribution und 2 von 3 Success-Metriken hängen an öffentlichem GitHub — genau das ist als E-3 „später" vertagt
|
||||
|
||||
**Bezug:** FR-1.1, S-1, S-3 (PRD §8), E-3 (PRD §10), J1, Spec §4.
|
||||
|
||||
- **HACS unterstützt ausschließlich GitHub-Repos** — auch als Custom Repository, erst recht für den Default-Store (S-3). FR-1.1 („als HACS-Custom-Integration installierbar") ist ohne öffentliches GitHub-Repo nicht erfüllbar.
|
||||
- S-1 misst *„≥ 100 GitHub-Stars"* — ohne GitHub-Repo per Definition 0.
|
||||
- Der Blueprint-Import-Flow (J1: *„importiert das Push-Blueprint"*) importiert bequem von GitHub/Community-Forum; von einem privaten Forgejo importiert kein Endkunde.
|
||||
|
||||
Gleichzeitig sagt E-3: *„Repo-Remote (Forgejo vs. neues GitHub) — später, vor Release"* — und der Haus-Standard (globale Konfig) ist derzeit Forgejo mit einem GitHub-Umbau „in Vorbereitung". Das ist keine Kleinentscheidung, die man vor Release nebenbei trifft: **Ohne öffentliches GitHub existiert der komplette Distributionskanal des Produkts nicht.** Der PRD behandelt seine einzige Vertriebsschiene als offene Randnotiz.
|
||||
|
||||
**Fix:** E-3 zur Blocker-Entscheidung hochstufen und vor Phase 3 entscheiden; im PRD festhalten: öffentliches GitHub-Repo ist Voraussetzung für FR-1.1, S-1, S-3 und den Blueprint-Import in J1.
|
||||
|
||||
---
|
||||
|
||||
## HOCH
|
||||
|
||||
### H1 — NFR-4-Arbeitshypothese „2024.12+" ist faktisch falsch: Config-Subentries gibt es erst seit HA 2025.3
|
||||
|
||||
**Bezug:** NFR-4, E-2 (PRD §10), FR-2.1, Spec §4.
|
||||
|
||||
Die Spec begründet 2024.12+ mit *„weather.get_forecasts mit return_response=True und Config-Sub-Entries"*. `get_forecasts` ja — aber **Config-Subentries (ConfigSubentryFlow) wurden erst mit HA Core 2025.3 eingeführt**. Das gesamte Kleiderschrank-Modell (FR-2.1: „Stücke als Sub-Entries") ist auf 2024.12 nicht baubar. Ja, NFR-4 sagt „wird in der Architekturphase verifiziert" — aber ein PRD, dessen Versions-Hypothese für sein zentrales Datenmodell nachweislich falsch ist, hat das Phase-2-Gate nicht verdient. Die falsche Zahl steht zudem als Vorschlag in E-2 und wandert so ungeprüft in die Benutzer-Entscheidung.
|
||||
|
||||
Folgewirkung: Min-Version 2025.3+ verkleinert die adressierbare Basis (konservative Installationen, Appliance-User mit Auto-Update aus) — das gehört als bewusste Zielgruppen-Konsequenz in den PRD, nicht als stille Korrektur in Phase 3.
|
||||
|
||||
**Fix:** E-2/NFR-4 auf ≥ 2025.3 korrigieren (und dann wirklich gegen die real genutzten Subentry-APIs verifizieren).
|
||||
|
||||
### H2 — Datenverfügbarkeits-Annahme: Der primäre Regel-Schlüssel (`gefühlt_max`) und halbe Regel-Tabelle fehlen beim HA-Standard-Wetter — „degradiert" ist der Normalfall, nicht der Edge-Case
|
||||
|
||||
**Bezug:** Addendum §3/§5, FR-4.3, J2, Spec §7.
|
||||
|
||||
Die gesamte Temperatur-Regelkette schlüsselt auf *„gefühlt_max"*. Die HA-Default-Wetterintegration (met.no) liefert in der Prognose **keine gefühlte Temperatur**; `uv_index` und Böen sind in Forecasts der meisten verbreiteten Provider nicht oder unzuverlässig belegt; Regenwahrscheinlichkeit ist providerabhängig. Konsequenz: Für den typischen Nutzer mit HA-Bordmitteln feuern die UV-Regel und die Böen-Regel nie, die Temperaturregeln laufen dauerhaft im Degradations-Pfad (FR-4.3), und das J2-Beispiel *„70 % Regen"* ist mit der Default-Wetterquelle nicht reproduzierbar.
|
||||
|
||||
Der PRD tut so, als sei fehlende Datenlage die Ausnahme („Fehlt ein Prognosefeld…"). Real ist sie der **Default-Zustand** des v1-Providers. Das heißt: Der Hinweis *„eingeschränkte Datenlage"* (FR-4.3) klebt bei vielen Installationen dauerhaft an jeder Empfehlung — das wirkt nach zwei Tagen wie ein kaputtes Produkt und erzeugt genau die Einrichtungs-Issues, die C-1 verbietet.
|
||||
|
||||
**Fix:** Regel-Defaults auf die real verfügbaren Felder ausrichten (Fallback-Kaskade gefühlt→gemessen als Normalpfad definieren, nicht als Degradation); pro Regel festlegen, welches Feld Pflicht ist; den Dauer-Hinweis nur zeigen, wenn eine Regel tatsächlich nicht bewertbar war; J2 mit Daten schreiben, die die v1-Quelle liefert.
|
||||
|
||||
### H3 — „Einrichtung < 15 min inkl. 5–15 Stücke" ist mit nativer Sub-Entry-UI nicht ehrlich; J4 „30 Sekunden" ebenso
|
||||
|
||||
**Bezug:** J1, J4, JTBD-3, S-2, FR-2.1.
|
||||
|
||||
Die reale J1-Kette: HACS-Repo hinzufügen → Download → **HA-Neustart** (von J1 unterschlagen, allein 1–3 min) → Config-Flow → **5–15 Subentry-Dialoge mit je bis zu 9 Feldern** (Name, Kategorie, Wärmegrad, wasserdicht, winddicht, Formalität, Temp-Bereich, Foto, aktiv) → Karte aufs Dashboard (siehe H4) → Blueprint importieren → Automation anlegen. Die Subentry-UI ist ein Klickpfad pro Stück über die Integrationsseite — realistisch 60–90 s pro Stück für jemanden, der die Felder zum ersten Mal sieht. 10 Stücke ≈ 10–15 min **nur fürs Inventar**. Das 15-Minuten-Versprechen steht dreifach im Dokument (JTBD-3, J1, S-2) und ist damit dreifach angreifbar; die Spec selbst listet in §15 den Zweifel (*„Ob v1 die Sub-Entry-CRUD-UX von HA praktisch trägt"*) — der PRD hat diesen offenen Punkt stillschweigend zur Zusage befördert. J4 (*„ergänzt das Stück in 30 Sekunden"*) beschreibt denselben 9-Felder-Dialog.
|
||||
|
||||
**Fix:** Entweder Budget ehrlich machen (z. B. „< 15 min bis zur ersten Empfehlung mit 5 Stücken", Restart einrechnen, Feldzahl im Anlage-Dialog auf 3–4 Pflichtfelder reduzieren, Rest optional/Defaults) oder einen Schnellstart definieren (vorbefüllte Beispiel-Garderobe / „Basis-Set anlegen"-Schritt im Config-Flow). S-2 als Messprotokoll präzisieren (Stoppuhr ab HACS-Klick inkl. Restart?).
|
||||
|
||||
### H4 — Lovelace-Karte: „zieht die Karte aufs Dashboard" versteckt einen manuellen Ressourcen-Schritt, den genau diese Zielgruppe nicht kann
|
||||
|
||||
**Bezug:** FR-7.3, J1, Addendum §1 (`www/what-to-wear-card.js`), Zielgruppe §2 (*„ohne Lust auf YAML-Bastelei"*).
|
||||
|
||||
Ein HACS-Repo hat **eine** Kategorie: Integration **oder** Dashboard-Plugin. Liegt die Karte im Integrations-Repo (`www/…`), registriert HACS weder statischen Pfad noch Lovelace-Ressource. Dann muss entweder die Integration das selbst tun (technisch möglich, aber nur im Storage-Mode zuverlässig, in YAML-Mode-Dashboards gar nicht — gehört als Anforderung ins Dokument) oder der Nutzer legt manuell eine Ressource an — exakt die YAML-Bastelei, die die Zielgruppendefinition ausschließt. Der PRD-Satz „zieht die WTW-Karte aufs Dashboard" (J1) behandelt den heikelsten UX-Schritt der ganzen Kette als gegeben. Das ist die klassische Stelle, an der „15 Minuten ohne Rückfrage" (S-2) reißt.
|
||||
|
||||
**Fix:** FR-7.3 ergänzen: Karte wird von der Integration automatisch als Ressource registriert (Storage-Mode), Verhalten im YAML-Mode dokumentiert; oder bewusste Entscheidung für ein zweites HACS-Repo (Plugin) mit den Konsequenzen im PRD.
|
||||
|
||||
### H5 — Retention ist der eigentliche Produktkiller und kommt im PRD nicht vor: repetitive Empfehlungen, tägliche Pushs ohne Relevanzfilter, keine Nutzungs-Metrik
|
||||
|
||||
**Bezug:** PRD §8, FR-7.4/7.5, JTBD-1, Addendum §4 (Rotation „v1 optional").
|
||||
|
||||
Deterministische Regeln + statischer 10-Stücke-Schrank = im Juli sechs Wochen lang jeden Abend um 20:00 „T-Shirt, kurze Hose, Sneaker". Rotation ist laut Addendum §4 *optional*, Wäschestatus ist Non-Goal (fein, Benutzerentscheidung) — aber der PRD zieht daraus keine Konsequenz: Es gibt **keinen Relevanzfilter** (z. B. Push nur bei Wetterumschwung/Lücke/Änderung zur Vorempfehlung), und der Push-Blueprint feuert bedingungslos täglich. Bedingungslose tägliche Pushs mit identischem Inhalt sind der schnellste Weg in „Benachrichtigung stummschalten" — und ein stummgeschaltetes WTW ist deinstalliert, es weiß es nur noch nicht.
|
||||
|
||||
Verschärfend: §8 misst ausschließlich Akquise (Installationen, Stars, Store-Aufnahme, Einrichtungszeit). **Keine einzige Metrik misst, ob irgendjemand das Produkt in Woche 3 noch benutzt.** Ohne Telemetrie (Benutzerentscheidung, akzeptiert) bleibt trotzdem Beta-Follow-up nach 14/30 Tagen als Messinstrument — der PRD sieht es nicht vor. So wie §8 dasteht, kann das Produkt alle Success-Metriken erfüllen und trotzdem ein Gimmick sein, das nach zwei Wochen niemand mehr liest.
|
||||
|
||||
**Fix:** (a) Relevanz-/Änderungslogik als FR oder mindestens als Blueprint-Option („nur benachrichtigen wenn: Lücke vorhanden / Empfehlung ≠ gestern / Warnbedingung"); (b) Rotation von „optional" zu v1-Pflicht bei Gleichstand; (c) S-Metrik ergänzen: „≥ 2 von 3 Beta-Testern nutzen die Empfehlung nach 30 Tagen noch (Selbstauskunft)".
|
||||
|
||||
---
|
||||
|
||||
## MITTEL
|
||||
|
||||
### M1 — Metrik-Qualität: S-1 ist per „oder" gameable, C-1 bei kleinen Zahlen bedeutungslos, S-2 ohne Rekrutierungsplan
|
||||
|
||||
**Bezug:** PRD §8.
|
||||
|
||||
- S-1: *„≥ 50 aktive Installationen … **oder** ≥ 100 GitHub-Stars"* — Stars misst Marketing/HN-Glück, nicht Nutzung; die Oder-Verknüpfung erlaubt „Erfolg" ganz ohne einen einzigen aktiven Nutzer. Zudem zählt HA-Analytics nur Opt-in-Haushalte — 50 gezählte Installationen bedeuten real ein Mehrfaches, was das Ziel härter macht als es aussieht; umgekehrt macht der Stars-Ausweg es beliebig weich. Entweder-oder in beide Richtungen kaputt.
|
||||
- C-1: *„< 30 % der Issues betreffen Einrichtung"* — bei realistischen 5–10 Issues in 60 Tagen entscheidet ein einzelnes Ticket über bestanden/durchgefallen. Ohne Mindest-n ist das Rauschen.
|
||||
- S-2: Woher kommen *„≥ 3 externe Beta-Tester"*? Kein Kanal, kein Zeitpunkt, kein Protokoll definiert. Ohne Plan ist das ein Feigenblatt, das kurz vor Release durch die eigenen Bekannten „bestanden" wird.
|
||||
|
||||
**Fix:** S-1 auf Installationszahl allein stellen (Stars als sekundäres Signal), C-1 mit Mindest-n versehen (z. B. „ab 10 Issues"), S-2 um Rekrutierungsweg + Protokollort ergänzen.
|
||||
|
||||
### M2 — „optional Foto" (FR-2.1) ist ein unbeziffertes Fass: Upload, Speicherort, Ausspielung in der Karte — alles offen
|
||||
|
||||
**Bezug:** FR-2.1, FR-7.3, Spec §15.
|
||||
|
||||
Die Spec listet Foto-Speicherung explizit als offenen Punkt (*„Foto-Speicherung bei nativem Kleiderschrank (Pfad/Umfang) — v1 optional/minimal"*); der PRD übernimmt „optional Foto" kommentarlos als FR. Ungeklärt: Wie kommt ein Foto durch einen Subentry-Flow (Datei-Upload im Config-Flow ist eng begrenzt)? Wo liegt es (Config-Entry-Storage ist JSON — Bilder gehören da nicht rein)? Zeigt die Karte es an (FR-7.3 schweigt)? Entweder ist das Feature in v1 ein toter Feld-Platzhalter (dann raus aus dem FR) oder es zieht ein eigenes Speicher-/Serving-Konzept nach sich (dann gehört das benannt).
|
||||
|
||||
**Fix:** Foto aus FR-2.1 streichen und zu v1.1/LagerLens schieben — oder minimal definieren (nur Pfad/URL-Referenz auf `/local/...`, keine Upload-UI, Karte zeigt es wenn vorhanden).
|
||||
|
||||
### M3 — Entity-Naming inkonsistent und un-i18n: PRD `sensor.what_to_wear_empfehlung` vs. Spec `sensor.what_to_wear`
|
||||
|
||||
**Bezug:** FR-7.1 vs. Spec §10, FR-8.1.
|
||||
|
||||
Der PRD tauft den Sensor mit deutschem Suffix (`_empfehlung`), die freigegebene Spec sagt `sensor.what_to_wear`. Für die englische Hälfte der Zielgruppe (§2: „DACH + international") ist eine deutsche Entity-ID ein Fremdkörper in jeder Automation und jedem Forum-Beispiel — und Entity-IDs lassen sich nach Release nicht mehr geräuschlos ändern. Kleinigkeit, aber genau die Sorte, die man vor dem ersten Release festnageln muss.
|
||||
|
||||
**Fix:** Englische/neutrale Entity-ID festlegen (z. B. `sensor.what_to_wear_recommendation` oder schlicht Spec-Variante), Anzeigename via Translations.
|
||||
|
||||
### M4 — FR-1.2 validiert nur „eine weather-Entität existiert", nicht „sie kann morgen": Der Fehlzustand FR-3.4 wird zum Erst-Erlebnis
|
||||
|
||||
**Bezug:** FR-1.2, FR-3.4, J1, S-2.
|
||||
|
||||
Der Entity-Selector prüft Existenz, nicht Prognosefähigkeit. Eine Entität ohne Daily-Forecast (nur hourly, oder Forecast-Support ganz fehlend) passiert den Config-Flow und liefert dann als allererstes Erlebnis den erklärenden Fehlzustand aus FR-3.4. Formal korrekt („kein Absturz"), aber J1/JTBD-3 versprechen ein *nützliches* Ergebnis in 15 Minuten — nicht eine korrekte Fehlermeldung. Der PRD definiert nicht, ob der Config-Flow die gewählte Entität aktiv testet (einmal `get_forecasts` aufrufen, Ergebnis prüfen) bevor er „fertig" meldet.
|
||||
|
||||
**Fix:** FR-1.2 ergänzen: Der Flow ruft die Prognose der gewählten Entität testweise ab und warnt/blockt, wenn kein Daily-/Hourly-Forecast für morgen ableitbar ist.
|
||||
|
||||
---
|
||||
|
||||
## Nicht gemeldet (bewusst)
|
||||
|
||||
- Alles aus der Benutzer-Entscheidungsliste (Open Source, Inventar echter Stücke, BYO-Key-LLM, v1-Wetterquelle, v1.1-Verschiebungen, de+en, keine Telemetrie).
|
||||
- Recorder-/Attributgrößen, Async-Details, Teststrategie-Feinheiten — Architektur-Phase, nicht PRD-Gate.
|
||||
- Kälteempfinden-Offset-Mechanik (±2 °C/Stufe) — plausibel genug für v1.
|
||||
307
docs/reviews/prd/review-edge-cases.md
Normal file
307
docs/reviews/prd/review-edge-cases.md
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
# Adversariales PRD-Review — Linse: Edge-Cases (Home Assistant)
|
||||
|
||||
- **Datum:** 2026-07-11
|
||||
- **Geprüfte Artefakte:**
|
||||
- `_bmad-output/planning-artifacts/prd.md` (PRD v1.0, draft)
|
||||
- `_bmad-output/planning-artifacts/prd-addendum.md` (technisches Addendum)
|
||||
- `docs/superpowers/specs/2026-07-11-what-to-wear-design.md` (freigegebener Design-Spec)
|
||||
- **Auftrag:** Für jeden Randfall prüfen: Deckt eine FR ihn ab, und ist das Verhalten definiert?
|
||||
- **Nicht geprüft/gemeldet (Benutzer-Entscheidungen):** Open-Source/kostenlos, echtes Inventar,
|
||||
BYO-Key-LLM, Wetterquelle v1 = HA-Entität, v1.1-Verschiebungen, de+en, keine Telemetrie.
|
||||
|
||||
---
|
||||
|
||||
## 1. Wetter-Randfälle
|
||||
|
||||
### 1.1 Regelwerk basiert auf `gefühlt_max` — das die meisten HA-Wetter-Entitäten gar nicht liefern → Dauer-Degradation im Normalfall (HOCH)
|
||||
|
||||
Addendum §3 schlüsselt die zentrale Temperaturregel auf `gefühlt_max`:
|
||||
|
||||
> „| gefühlt_max < 0 °C | wärme_min:5, mütze, handschuhe, schal |"
|
||||
|
||||
Im HA-Forecast-Schema ist `apparent_temperature` (gefühlte Temperatur) ein **optionales** Feld,
|
||||
das verbreitete Integrationen (Met.no = HA-Default-Wetter, viele andere) in der Prognose
|
||||
**nicht** liefern. Konsequenz nach FR-3.3/FR-4.3: Das Feld ist „fehlend", die wichtigste Regel
|
||||
der Engine läuft für die Mehrheit der Installationen **immer** degradiert, und jede Empfehlung
|
||||
trüge dauerhaft den Hinweis „eingeschränkte Datenlage" (FR-4.3). Das widerspricht dem
|
||||
Qualitätsanspruch von J2 und würde S-2 (Beta-Tester ohne Rückfrage) gefährden.
|
||||
|
||||
**Fehlt:** Ein definierter Fallback `gefühlt → temp` (ggf. Wind-Chill-Näherung aus temp+wind
|
||||
oder schlicht temp_min/max), und die Abgrenzung, wann „degradiert" wirklich kommuniziert wird
|
||||
(fehlender UV-Index ≠ fehlende Temperatur). Weder FR-4 noch Addendum §3/§5 definieren das.
|
||||
|
||||
### 1.2 Bezugswert der Temperaturbänder unklar — Tagesmax widerspricht dem Morgen-Fokus (HOCH)
|
||||
|
||||
Addendum §3 nennt für die Bänder nur „0–8 °C", „8–15 °C" usw., ohne zu sagen, **welcher** Wert
|
||||
gemeint ist (temp_min? temp_max? gefühlt_min?). Nur die erste Zeile nennt `gefühlt_max`.
|
||||
Gleichzeitig verspricht JTBD-1 explizit: „welche meiner Kleidungsstücke **zum Morgenwetter**
|
||||
passen". Ein Herbsttag mit 3 °C um 7:00 und 17 °C um 15:00 fällt per Tages-`max` ins Band
|
||||
„15–22 → wärme_min:2" — der Pendler friert um 7:00. Addendum §5 aggregiert ausdrücklich
|
||||
„stündlich→täglich", verliert also genau die Morgeninformation, für die das Produkt laut
|
||||
Vision/JTBD gebaut wird.
|
||||
|
||||
**Fehlt:** Festlegung, welcher Prognosewert die Wärmebänder treibt (z. B. gefühlt_min bzw.
|
||||
Morgenfenster 6–9 Uhr, wenn hourly vorhanden), und wie der Fall „großer Tagesgang" behandelt
|
||||
wird (Zwiebel-Hinweis?). Keine FR deckt das; die Fixtures in NFR-3 enthalten keinen
|
||||
Tagesgang-Fall.
|
||||
|
||||
### 1.3 Nur-daily-Forecast ohne hourly / Prognose deckt morgen nur teilweise ab (MITTEL)
|
||||
|
||||
FR-3.1 liest „tägliche und (falls verfügbar) stündliche" Prognosen; FR-3.4 deckt nur den
|
||||
Totalausfall („gar keine Prognose für morgen"). Undefiniert bleiben:
|
||||
|
||||
- **daily-only-Pfad:** Welche Normfelder gelten dann als „vorhanden"? Daily liefert typisch
|
||||
temperature/templow/condition/precipitation(_probability), aber oft kein `wind_gust_speed`,
|
||||
kein `uv_index`, keine gefühlte Temperatur. Läuft dann die Böen-Regel stumm nie? (FR-4.3
|
||||
sagt „degradiert + Hinweis" — aber ein Hinweis bei *jedem* daily-only-Nutzer ist wieder 1.1.)
|
||||
- **Teilabdeckung:** hourly reicht z. B. nur bis morgen 12:00 (viele Provider liefern 24–48 h
|
||||
ab jetzt). Ist „morgen" dann vorhanden, teilweise vorhanden oder fehlend? Addendum §5
|
||||
(„Aggregation strikt nach lokalem Kalenderdatum") definiert keine Mindestabdeckung.
|
||||
- **Mischbetrieb:** daily und hourly widersprechen sich (daily sagt Regen 70 %, hourly-Summe
|
||||
0,2 mm) — Vorrangregel nicht definiert.
|
||||
|
||||
### 1.4 Wetter-Entität nach der Einrichtung gelöscht / umbenannt / dauerhaft `unavailable` (HOCH)
|
||||
|
||||
FR-1.2 behandelt nur den Erstlauf ohne Entität. Für den Betrieb danach gibt es keinerlei FR:
|
||||
|
||||
- Nutzer benennt `weather.home` in `weather.zuhause` um → der Config-Entry zeigt auf eine
|
||||
tote entity_id, für immer.
|
||||
- Nutzer deinstalliert die Wetter-Integration → Entität weg.
|
||||
- Entität steht auf `unavailable`/`unknown` (Cloud-Ausfall, Gerät offline).
|
||||
|
||||
FR-3.4 („liefert … gar keine Prognose → erklärender Fehlzustand") deckt bestenfalls den
|
||||
Unavailable-Fall implizit; gelöscht/umbenannt ist ein Konfigurationsproblem, kein
|
||||
Prognoseproblem. HA-üblich wäre hier ein **Repair-Issue** bzw. Reauth/Reconfigure-Hinweis
|
||||
plus definierter Sensor-Zustand. Weder PRD noch Addendum noch Spec §12 erwähnen den Fall;
|
||||
auch die Teststrategie (Addendum §7) testet nur „keine Wetter-Entität beim Config-Flow".
|
||||
|
||||
### 1.5 °F/mph/inch-Entität (abgedeckt — ein Rest offen)
|
||||
|
||||
INV-3 + Spec §12 („Einheiten … bewusst konvertieren") + Unit-Tests „normalize (Einheiten…)"
|
||||
decken den Kern ab. **Offener Rest:** Nirgends steht, *woher* die Einheiten kommen (die
|
||||
Forecast-Service-Antwort enthält keine Einheiten; sie stehen in den Entity-Attributen
|
||||
`temperature_unit` etc.). Das ist Architektur-Detail, sollte aber in Phase 3 landen —
|
||||
kein eigenes Finding, da INV-3 die Anforderung („nie raten") sauber setzt.
|
||||
|
||||
### 1.6 Extreme/unplausible Werte & mehrdeutige Bandgrenzen (MITTEL)
|
||||
|
||||
Addendum §3:
|
||||
|
||||
> „| 0–8 °C | wärme_min:4 | / | 8–15 °C | wärme_min:3 |"
|
||||
|
||||
- **8 °C, 15 °C, 22 °C liegen in je zwei Bändern** — offen/geschlossen ist nicht definiert.
|
||||
Zusammen mit dem Offset (±2 °C je Stufe) entsteht Testbarkeits-Ambiguität.
|
||||
- **Keine Plausibilitätsgrenzen:** Eine defekte Entität, die 999 °C oder −273 °C liefert,
|
||||
wird von keiner Regel abgefangen; INV-3 verbietet nur das *Raten*, nicht das ungeprüfte
|
||||
Übernehmen. „Fehlend ≠ 0" ist definiert, „unplausibel = ?" nicht.
|
||||
- **Nach oben offen:** > 22 °C ist die letzte Stufe. 38 °C Hitzewelle erzeugt dieselben
|
||||
Requirements wie 23 °C (kein Hitze-Requirement analog zur Frost-Zeile; UV nur, wenn
|
||||
uv_index vorhanden — vgl. 1.3).
|
||||
- Gewitter ist im `zustand`-Enum (Addendum §5), hat aber **keine Regelzeile** (Schnee hat eine).
|
||||
|
||||
### 1.7 Mehrere Wetter-Entitäten
|
||||
|
||||
Erstauswahl per Entity-Selector (FR-1.2) und Wechsel im Options-Flow (FR-1.4) sind sauber
|
||||
definiert. Kein Finding — der problematische Mehrfach-Fall ist der der Config-Entries (§4.3).
|
||||
|
||||
---
|
||||
|
||||
## 2. Kleiderschrank-Randfälle
|
||||
|
||||
### 2.1 Kein Basis-Outfit-Konzept: an milden, trockenen Tagen ist das Kernergebnis undefiniert (KRITISCH)
|
||||
|
||||
Die Regeltabelle (Addendum §3) erzeugt Requirements **nur aus Wetter-Triggern**. Beispiel
|
||||
milder Tag, 18 °C, trocken, windstill: einziges Requirement ist `wärme_min:2`. FR-5.1 wählt
|
||||
„**je Requirement** das am besten passende aktive Stück" — also **ein** Stück. Woher kommen
|
||||
Hose, Oberteil, Schuhe? J2 verspricht ausdrücklich ein komplettes Outfit:
|
||||
|
||||
> „leg raus: Regenjacke, Wollpullover (Wärme 4), **Jeans, feste Schuhe**"
|
||||
|
||||
Keine Regel in §3 erzeugt jemals ein Hosen- oder Oberteil-Requirement; `feste_schuhe` kommt
|
||||
nur bei Regen. Es fehlt das Konzept „vollständiges Outfit" (welche Kategorien immer besetzt
|
||||
werden: Oberteil, Hose, Schuhe, + wetterbedingte Schichten) und jede Layering-Semantik:
|
||||
Bezieht sich `wärme_min:4` auf das wärmste Einzelstück, die Jacke, die Summe der Schichten?
|
||||
Darf der Matcher Pullover **und** Jacke kombinieren? FR-4/FR-5 und Addendum §3/§4 lassen das
|
||||
vollständig offen — das ist keine Randnotiz, sondern die Kernsemantik des Produkts, und sie
|
||||
ist am *einfachsten* Tag (kein Wetter-Trigger) am wenigsten definiert.
|
||||
|
||||
### 2.2 Requirements ohne Gegenstück im Stück-Schema (HOCH)
|
||||
|
||||
Die Regeln emittieren `feste_schuhe`, `profilsohle`, `sonnenschutz`, `schal`, `mütze`,
|
||||
`handschuhe` (Addendum §3). Das Stück-Schema (FR-2.1) kennt aber nur:
|
||||
|
||||
> „Name, Kategorie (Jacke/Pullover/Oberteil/Hose/Schuhe/Kopf/Hand/Hals/Accessoire),
|
||||
> Wärmegrad 1–5, wasserdicht, winddicht, Formalität, optional Temperaturbereich, Foto, aktiv"
|
||||
|
||||
- `profilsohle`: **kein** Attribut, keine Kategorie → das Requirement ist prinzipiell
|
||||
unerfüllbar; jeder Schneetag erzeugt zwangsläufig eine Lücke, egal was im Schrank liegt.
|
||||
- `feste_schuhe`: Was qualifiziert ein Stück der Kategorie „Schuhe" als „fest"? wasserdicht=ja?
|
||||
- `sonnenschutz`: Sonnenbrille/Cap = Kategorie „Accessoire" oder „Kopf"? Woran erkennt der
|
||||
Matcher Sonnenschutz-Eignung? (Ein Wintermützen-Stück ist auch „Kopf".)
|
||||
- `mütze`/`handschuhe`/`schal` ↔ Kategorien „Kopf/Hand/Hals": plausibel, aber nirgends als
|
||||
Mapping festgeschrieben — eine dünne Ballonmütze (Wärme 1) erfüllt dann formal das
|
||||
Frost-Requirement `mütze`.
|
||||
|
||||
Das Requirement→Eigenschaften/Kategorie-Mapping ist nirgends definiert (weder FR-5 noch
|
||||
Addendum §4). Ohne dieses Mapping ist FR-5.2 (benannte Lücken) nicht testbar.
|
||||
|
||||
### 2.3 Leerer Schrank: abgedeckt — „alle inaktiv" und „alle rausgefiltert" nicht (MITTEL)
|
||||
|
||||
FR-2.3 deckt den **leeren** Schrank explizit ab (gut). Nicht definiert:
|
||||
|
||||
- **Alle Stücke `aktiv = nein`:** Zählt das als „leer" (Hinweis auf leeren Schrank) oder als
|
||||
„N Lücken"? Für den Nutzer ist die richtige Botschaft eine andere („alles deaktiviert" vs.
|
||||
„nichts hinterlegt").
|
||||
- **Alle Stücke per Temperaturbereich ausgefiltert** (nur Sommer-Sachen im Winter): Addendum §4
|
||||
macht den Temp-Range zum **Hard-Filter** („muss zur Prognose passen") — gegen *welchen*
|
||||
Prognosewert (temp_min? max? gefühlt?) und mit welcher Grenzlogik, ist offen. Im Ergebnis
|
||||
besteht die Empfehlung nur aus Lücken; ob dann der FR-2.3-artige Sammelhinweis oder acht
|
||||
Einzellücken erscheinen, ist undefiniert. Genau dieser Fall (Saisonwechsel im Oktober) ist
|
||||
im Feld häufig.
|
||||
|
||||
### 2.4 Widersprüchliche/kaputte Stück-Attribute — keine Validierung definiert (MITTEL)
|
||||
|
||||
FR-2.1 definiert Felder, aber keine Validierungsregeln: Temperaturbereich mit min > max,
|
||||
leerer Name, Duplikate (zwei „Regenjacke"), Wärme 5 mit Temp-Range 20–30 °C, wasserdichte
|
||||
„Hose" fürs Requirement `wasserdichte_außenschicht` (zählt eine Hose als Außenschicht?).
|
||||
Nichts davon ist als Config-Flow-Validierung oder als definiertes Matcher-Verhalten
|
||||
festgehalten; die Teststrategie (Addendum §7) enthält keinen Fixture „inkonsistentes Stück".
|
||||
|
||||
### 2.5 200 Stücke
|
||||
|
||||
Kein hartes Finding: Der Matcher ist lokal und rein rechnerisch, die Sensor-Attribute
|
||||
enthalten nur die *gewählten* Stücke. Rest-Risiko ist die Sub-Entry-UX bei großen Beständen —
|
||||
im Spec §15 als offener Punkt benannt („ob v1 die Sub-Entry-CRUD-UX praktisch trägt").
|
||||
Damit ausreichend adressiert; ggf. in Beta beobachten (C-1).
|
||||
|
||||
---
|
||||
|
||||
## 3. HA-Umgebungs-Randfälle
|
||||
|
||||
### 3.1 Neustart kurz vor dem 20:00-Push / Startup-Race / Stale-Daten (HOCH)
|
||||
|
||||
FR-7.5 fordert Aktualisierung „mind. stündlich sowie beim HA-Neustart". Nicht definiert:
|
||||
|
||||
- **Startup-Race:** HA startet 19:58 neu; um 20:00 feuert das Push-Blueprint. Die
|
||||
Wetter-Integration ist evtl. noch nicht fertig geladen → erster Coordinator-Refresh
|
||||
schlägt fehl oder Entität ist noch `unavailable`. Was pusht das Blueprint dann —
|
||||
„unavailable", die **restaurierte gestrige** Empfehlung, gar nichts? Keine FR definiert es.
|
||||
- **Stale-Verhalten allgemein:** Wenn der stündliche Refresh fehlschlägt (FR-3.4-Fall), bleibt
|
||||
die letzte Empfehlung stehen? Wie alt darf sie sein, und markiert der Push das Alter?
|
||||
FR-7.1 hat zwar ein „Stand"-Attribut, aber kein FR fordert, dass Blueprint/Karte veraltete
|
||||
Stände kennzeichnen. INV-1 („nie stumm") braucht hier ein Gegenstück „nie *unmarkiert alt*".
|
||||
- **Blueprint-Inhalt:** Ob das Push-Blueprint den Sensor-State liest oder erst
|
||||
`what_to_wear.recommend` aufruft und auf das Event wartet, ist offen — das entscheidet
|
||||
über Frische und über das Race-Verhalten.
|
||||
|
||||
### 3.2 Blueprint feuert während einer Neuberechnung (MITTEL, in 3.1 enthalten)
|
||||
|
||||
`what_to_wear.recommend` (FR-7.2) und der stündliche Coordinator-Lauf können kollidieren
|
||||
(Nutzer drückt „neu berechnen", während 20:00-Push liest). HA-State-Schreiben ist atomar,
|
||||
aber Event- vs. Sensor-Konsistenz und Parallel-Aufrufe des Service (Debounce? Queue?) sind
|
||||
nicht spezifiziert. Niedrigere Dringlichkeit, gehört aber in die Architektur-Invarianten.
|
||||
|
||||
### 3.3 Sprachwechsel de↔en zur Laufzeit (MITTEL)
|
||||
|
||||
FR-8.1/FR-1.3: „folgt der HA-Sprache". Undefiniert:
|
||||
|
||||
- **Zeitpunkt der Bindung:** Wird die Sprache je Berechnung gelesen oder bei Setup
|
||||
eingefroren? Nach einem Wechsel en→de bleibt die gecachte Empfehlung bis zum nächsten
|
||||
Refresh in der alten Sprache — Karte de, Push en gemischt.
|
||||
- **Spec↔PRD-Widerspruch:** Spec §11 sieht eine explizite Sprachwahl im Config-Flow vor
|
||||
(„Sprache (Default = HA-Sprache)"), das PRD nur noch implizites Folgen der HA-Sprache.
|
||||
Eine der beiden Aussagen muss weichen.
|
||||
- **Entity-ID:** FR-7.1 legt `sensor.what_to_wear_empfehlung` fest — ein **deutsches** Wort
|
||||
in der Entity-ID für alle en-Installationen (und Diskrepanz zum Spec §10:
|
||||
`sensor.what_to_wear`). Entity-IDs sind quasi-API (Blueprints, Karten, Doku) und sollten
|
||||
sprachneutral sein; der Widerspruch Spec↔PRD ist zudem ein Konsistenzfehler im Artefakt.
|
||||
|
||||
### 3.4 Zeitzonenwechsel / Reisen
|
||||
|
||||
INV-2 bindet „morgen" an `hass.config.time_zone`, FR-3.2 macht DST explizit, FR-7.5
|
||||
aktualisiert stündlich — ein TZ-Wechsel wird also spätestens nach einer Stunde korrekt
|
||||
verarbeitet. Ausreichend definiert, kein Finding. (Der Fall „Wetter-Entität zeigt einen
|
||||
anderen Ort als die HA-TZ" ist ein Nutzerkonfigurationsproblem außerhalb des v1-Scopes.)
|
||||
|
||||
---
|
||||
|
||||
## 4. Lebenszyklus-Randfälle
|
||||
|
||||
### 4.1 Entfernen der Integration = stiller Totalverlust des gepflegten Kleiderschranks (MITTEL)
|
||||
|
||||
Die Stücke leben als Sub-Entries **im Config-Entry** (FR-2.1, Addendum §1). Löscht der Nutzer
|
||||
den Config-Entry (oder entfernt/reinstalliert die Integration zur Fehlersuche — bei HACS
|
||||
üblich), ist das mühsam gepflegte Inventar weg. Kein FR erwähnt Export/Import, eine Warnung
|
||||
oder auch nur einen Doku-Hinweis „HA-Backup deckt das ab". Für ein Produkt, dessen Kernwert
|
||||
das gepflegte Inventar ist (J4), fehlt hier eine bewusste Entscheidung.
|
||||
|
||||
### 4.2 Update der Integration / Schema-Migration (MITTEL)
|
||||
|
||||
FR-1.4 macht Schwellwerte konfigurierbar, Optionen und Sub-Entry-Schema werden sich über
|
||||
Versionen ändern. Kein Wort zu Config-Entry-/Sub-Entry-**Migration** (HA-Versionierung von
|
||||
Entries) — bei einem HACS-Produkt mit Bestandsnutzern Pflicht ab dem zweiten Release.
|
||||
Zusätzlich konkret: Die Arbeitshypothese Min-HA **2024.12+** (NFR-4, E-2) ist für
|
||||
Config-**Sub-Entries** sehr wahrscheinlich falsch — Subentry-Support kam erst mit HA Core
|
||||
**2025.3**. NFR-4 sieht die Verifikation zwar vor, aber die Hypothese sollte schon im PRD
|
||||
nicht falsch verankert sein (sie steckt auch in Spec §4).
|
||||
|
||||
### 4.3 Mehrere Config-Entries (MITTEL)
|
||||
|
||||
Nirgends steht, ob v1 **einen** oder **mehrere** Config-Entries erlaubt. Da Mehrpersonen
|
||||
explizit v1.1 ist, werden Nutzer genau das per zweitem Entry nachbauen („einer für mich,
|
||||
einer fürs Kind"). Folgen ohne Definition: Kollision/Suffixe bei
|
||||
`sensor.what_to_wear_empfehlung`, Blueprint-Bezug auf „den" Sensor, doppelte stündliche
|
||||
Forecast-Aufrufe. v1 sollte explizit `single_config_entry` festschreiben **oder** das
|
||||
Mehrfach-Verhalten definieren. Keine FR deckt es; Addendum §7 testet es nicht.
|
||||
|
||||
### 4.4 Lovelace-Karte als Teil der Integration (Randnotiz, Architekturphase)
|
||||
|
||||
`www/what-to-wear-card.js` wird mit der *Integration* ausgeliefert (Addendum §1). HACS
|
||||
registriert Dashboard-Ressourcen nur für Plugin-Repos, nicht für Integrationen — d. h. ohne
|
||||
eigene Ressourcen-Registrierung durch die Integration scheitert J1-Schritt „zieht die
|
||||
WTW-Karte aufs Dashboard" für Laien. Kein PRD-Finding im engen Sinn (technisches „Wie"),
|
||||
aber J1/S-2 hängen daran; gehört zwingend auf die Architektur-Checkliste.
|
||||
|
||||
---
|
||||
|
||||
## 5. Abdeckungs-Matrix (Kurzform)
|
||||
|
||||
| Randfall | Abgedeckt? | Wo / Lücke |
|
||||
|---|---|---|
|
||||
| daily-only ohne hourly | teilweise | FR-3.1 liest; Felder-/Abdeckungssemantik offen (→1.3) |
|
||||
| °F/mph | ja | INV-3, Spec §12, Tests |
|
||||
| Extremwerte/Plausibilität | nein | →1.6 |
|
||||
| Prognose reicht nicht bis morgen | Totalausfall ja (FR-3.4) | Teilabdeckung offen (→1.3) |
|
||||
| Entität umbenannt/gelöscht/unavailable | nein | →1.4 |
|
||||
| Mehrere Wetter-Entitäten | ja | FR-1.2/1.4 |
|
||||
| 0 Stücke | ja | FR-2.3 |
|
||||
| 200 Stücke | ausreichend | Spec §15 offener Punkt |
|
||||
| Alle inaktiv | nein | →2.3 |
|
||||
| Widersprüchliche Attribute | nein | →2.4 |
|
||||
| Nur Sommer-Sachen im Winter | teilweise | Lücken ja, Filter-Semantik offen (→2.3) |
|
||||
| Neustart vor Push | teilweise | FR-7.5 nennt Neustart; Race/Stale offen (→3.1) |
|
||||
| Sprachwechsel zur Laufzeit | nein | →3.3 |
|
||||
| TZ-Wechsel/Reisen | ja | INV-2, FR-3.2, FR-7.5 |
|
||||
| Blueprint während Neuberechnung | nein | →3.2 |
|
||||
| Update/Migration | nein | →4.2 |
|
||||
| Entfernen/Datenverlust | nein | →4.1 |
|
||||
| Mehrere Config-Entries | nein | →4.3 |
|
||||
| Milder Tag / komplettes Outfit | **nein** | →2.1 (kritisch) |
|
||||
| Requirement↔Schema-Mapping | **nein** | →2.2 |
|
||||
|
||||
---
|
||||
|
||||
## 6. Priorisierte Findings (Zusammenfassung)
|
||||
|
||||
1. **KRITISCH** — Kein Basis-Outfit-/Layering-Konzept; Kernergebnis an milden Tagen undefiniert (§2.1).
|
||||
2. **HOCH** — Requirements (`profilsohle`, `feste_schuhe`, `sonnenschutz` …) ohne Mapping aufs Stück-Schema (§2.2).
|
||||
3. **HOCH** — Wetter-Entität nach Einrichtung gelöscht/umbenannt/unavailable: kein definiertes Verhalten (§1.4).
|
||||
4. **HOCH** — Regeln basieren auf meist fehlendem `gefühlt_max`; Fallback undefiniert → Dauer-Degradation (§1.1).
|
||||
5. **HOCH** — Bezugswert der Temperaturbänder (min/max/Morgen) offen; widerspricht JTBD-1 „Morgenwetter" (§1.2).
|
||||
6. **HOCH** — Startup-Race & Stale-Daten rund um den 20:00-Push undefiniert (§3.1, §3.2).
|
||||
7. **MITTEL** — Mehrere Config-Entries nicht geregelt (§4.3).
|
||||
8. **MITTEL** — Bandgrenzen mehrdeutig, keine Plausibilitätsprüfung, keine Hitze-/Gewitter-Regel (§1.6).
|
||||
9. **MITTEL** — daily-only-/Teilabdeckungs-Semantik der Prognose offen (§1.3).
|
||||
10. **MITTEL** — „Alle inaktiv"/Temp-Range-Hardfilter-Semantik/Validierung der Stücke offen (§2.3, §2.4).
|
||||
11. **MITTEL** — Lebenszyklus: Entfernen löscht Inventar ohne Warnung/Export; keine Migrations-Anforderung; Min-HA-Hypothese 2024.12 kollidiert mit Sub-Entries (≈2025.3) (§4.1, §4.2).
|
||||
12. **MITTEL** — Sprachbindung zur Laufzeit + deutsche Entity-ID + Spec↔PRD-Widersprüche (Sensorname, Sprachwahl) (§3.3).
|
||||
259
docs/reviews/prd/review-rubrik.md
Normal file
259
docs/reviews/prd/review-rubrik.md
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
# Adversariales PRD-Review — Linse „Systematische Qualität (Rubrik)"
|
||||
|
||||
- **Datum:** 2026-07-11
|
||||
- **Reviewer:** unabhängige Senior-Product-Review-Linse (Rubrik: Struktur-Vollständigkeit,
|
||||
Testbarkeit jeder FR, Konsistenz PRD ↔ Addendum ↔ Spec, Messbarkeit der Metriken, Eindeutigkeit)
|
||||
- **Geprüfte Artefakte:**
|
||||
- `_bmad-output/planning-artifacts/prd.md` (PRD v1.0, draft)
|
||||
- `_bmad-output/planning-artifacts/prd-addendum.md` (technisches Addendum)
|
||||
- `docs/superpowers/specs/2026-07-11-what-to-wear-design.md` (Design-Spec v1)
|
||||
- **Nicht bewertet (Benutzer-Entscheidungen, per Auftrag ausgenommen):** Open-Source/kostenlos,
|
||||
Inventar echter Stücke, BYO-Key-LLM default aus, Wetterquelle v1 = HA-Entität, Vertagung von
|
||||
LagerLens/Open-Meteo/CRUD-Karte/Mehrpersonen, de+en, keine Telemetrie.
|
||||
|
||||
---
|
||||
|
||||
## 1. Struktur-Vollständigkeit (Rubrik-Punkt 1)
|
||||
|
||||
Alle geforderten Bausteine sind vorhanden: Vision (§1), JTBD (§2, drei Jobs), Journeys (§3,
|
||||
J1–J4), Glossar (§4), FRs (§5, FR-1 bis FR-8), Non-Goals (§6), MVP-Scope (§7), Metriken (§8),
|
||||
NFRs (§9), plus offene Entscheidungen (§10) und Referenzen (§11). **Strukturell keine Lücke.**
|
||||
Die Substanz einzelner Abschnitte hat jedoch Löcher — siehe unten.
|
||||
|
||||
---
|
||||
|
||||
## 2. KRITISCHE Findings
|
||||
|
||||
### F-1 (KRITISCH) — Outfit-Kompositionslogik unspezifiziert; FR-5.1 erzeugt nicht das versprochene Ergebnis
|
||||
|
||||
**Bezug:** FR-5.1, J2, Addendum §3/§4, Spec §7/§8.
|
||||
|
||||
FR-5.1: *„Je Requirement wird das am besten passende aktive Stück gewählt."* Die Regel-Engine
|
||||
(Addendum §3) erzeugt aber nur wetter-getriebene Requirements (`wärme_min:X`,
|
||||
`wasserdichte_außenschicht`, `winddicht`, `mütze`, …). **Kein Regelwerk erzeugt jemals ein
|
||||
Requirement „Hose" oder „Oberteil".** Journey J2 verspricht dagegen ein komplettes Outfit:
|
||||
*„leg raus: Regenjacke, Wollpullover (Wärme 4), Jeans, feste Schuhe"* — die **Jeans kann aus
|
||||
keinem Requirement stammen**.
|
||||
|
||||
Zwei unvereinbare Lesarten von FR-5.1:
|
||||
1. Nur requirement-getriebene Stücke → an einem milden, trockenen Tag besteht die Empfehlung aus
|
||||
genau einem Pullover (wärme_min:2) und sonst nichts. Widerspricht J2/J3 und der Vision
|
||||
(„welche Kleidung morgen passt").
|
||||
2. Implizites Basis-Outfit (je Körperzone ein Stück) + wetter-getriebene Ergänzungen → nirgends
|
||||
spezifiziert: Welche Kategorien sind Pflichtzonen? Wie viele Stücke pro Zone (Layering:
|
||||
Pullover *und* Jacke bei wärme_min:4)? Auf welche Kategorie wird `wärme_min` angewandt —
|
||||
Pullover, Jacke, Summe der Schichten?
|
||||
|
||||
Der Kern-Algorithmus des Produkts ist damit auf zwei Arten lesbar und in keiner Lesart
|
||||
vollständig — Phase-2-Gate-Blocker.
|
||||
|
||||
**Fix-Vorschlag:** Explizite FR ergänzen: Empfehlung = Basis-Outfit (definierte Pflichtkategorien,
|
||||
z. B. Oberteil + Hose + Schuhe, je genau 1 Stück) + wetter-getriebene Zusatz-Requirements;
|
||||
Layering-Semantik von `wärme_min` festlegen (z. B. „wärmstes Oberteil-/Jacken-Stück ≥ X" oder
|
||||
Schichtsumme) und J2 daran ausrichten.
|
||||
|
||||
### F-2 (KRITISCH) — Requirement-Eigenschaften sind gegen das Stück-Datenmodell nicht auflösbar
|
||||
|
||||
**Bezug:** FR-2.1, FR-5.1/5.2, Addendum §3/§4/§5, Spec §6/§7.
|
||||
|
||||
Die Regeltabelle erzeugt Requirements, für die **im Stück-Modell (FR-2.1) keine matchbare
|
||||
Eigenschaft existiert**:
|
||||
|
||||
| Requirement (Addendum §3) | Matchbare Stück-Eigenschaft? |
|
||||
|---|---|
|
||||
| `feste_schuhe` | ✗ — es gibt Kategorie „Schuhe", aber kein Feld „fest" |
|
||||
| `profilsohle` | ✗ — kein Feld |
|
||||
| `sonnenschutz` | ✗ — keine Kategorie/kein Feld (Sonnenbrille? Cap?) |
|
||||
| `mütze` / `handschuhe` / `schal` | teils — Kategorien heißen „Kopf/Hand/Hals"; das Mapping Requirement → Kategorie ist **nirgends definiert** |
|
||||
| `wasserdichte_außenschicht` | teils — `wasserdicht` existiert, aber welche Kategorien gelten als „Außenschicht"? |
|
||||
|
||||
Konsequenz je nach Implementierungs-Raten: Entweder werden diese Requirements still ignoriert
|
||||
(verletzt INV-1/FR-5.2) oder sie erzeugen **immer** eine Lücke, selbst wenn der Nutzer
|
||||
Wanderstiefel im Schrank hat (J2 würde bei jedem Regentag „Lücke: feste Schuhe" melden). FR-5.1
|
||||
und FR-5.2 sind so nicht objektiv testbar, weil die Grundwahrheit „passendes Stück existiert"
|
||||
undefiniert ist.
|
||||
|
||||
**Fix-Vorschlag:** Verbindliche Mapping-Tabelle Requirement-Eigenschaft → (Kategorie[n] +
|
||||
Stück-Feld[er]) in PRD oder Addendum; Requirements streichen oder Stück-Modell erweitern, wo kein
|
||||
Mapping möglich ist (z. B. `feste_schuhe`/`profilsohle` zu einem bool „robust/profiliert" am
|
||||
Schuh machen — oder aus den Regeln entfernen).
|
||||
|
||||
---
|
||||
|
||||
## 3. HOHE Findings
|
||||
|
||||
### F-3 (HOCH) — muss/soll/kann wird nie zugewiesen → FR-5.2 untestbar
|
||||
|
||||
**Bezug:** Glossar („Lücke", „Requirement"), FR-5.1, FR-5.2, Addendum §3, Spec §6.
|
||||
|
||||
Requirements tragen laut Glossar und Spec §6 eine Priorität (muss/soll/kann); FR-5.1 sortiert
|
||||
danach, FR-5.2 erzeugt Lücken **nur** für muss/soll. Aber: **Die Regeltabelle (Addendum §3) weist
|
||||
keinem einzigen erzeugten Requirement eine Priorität zu.** Ist `sonnenschutz` „kann" und
|
||||
`wasserdichte_außenschicht` „muss"? Ohne diese Zuordnung ist nicht entscheidbar, welche
|
||||
unerfüllten Requirements eine Lücke erzeugen — FR-5.2 hat keinen prüfbaren Erwartungswert.
|
||||
|
||||
**Fix-Vorschlag:** Prioritätsspalte in der Regeltabelle ergänzen (auch wenn Defaults „illustrativ"
|
||||
sind, muss die *Struktur* der Zuordnung feststehen).
|
||||
|
||||
### F-4 (HOCH) — FR-4.1 verweist auf „dokumentierte Defaults", die sich selbst als unverbindlich erklären; Grenzwerte doppelt belegt
|
||||
|
||||
**Bezug:** FR-4.1, Addendum §3, Spec §7.
|
||||
|
||||
FR-4.1: Schwellwerte *„haben dokumentierte Defaults (Addendum §3)"*. Addendum §3 überschreibt
|
||||
sich selbst: *„illustrativ; final in Architektur/Implementierung"*. Damit referenziert eine
|
||||
verbindliche FR ein ausdrücklich unverbindliches Dokument — der Abnahmetest für FR-4.1 hat keine
|
||||
Soll-Werte. Zusätzlich sind die Temperaturintervalle mehrdeutig: **8 °C liegt sowohl in „0–8" als
|
||||
auch in „8–15"** (ebenso 15 und 22); bei exakt 8 °C ist das Verhalten unbestimmt. Und die Zeile
|
||||
„> 22 °C → wärme_min:1, **ggf.** sonnenschutz" ist mit „ggf." nicht deterministisch — Widerspruch
|
||||
zu FR-4.1 („deterministisch erzeugt").
|
||||
|
||||
**Fix-Vorschlag:** Entweder Defaults im Addendum für v1 verbindlich setzen (Architektur darf
|
||||
begründet ändern) oder FR-4.1 umformulieren („Defaults werden in der Architektur festgelegt und
|
||||
dokumentiert"). Intervalle halboffen notieren (z. B. `[0, 8)`), „ggf." durch eine Bedingung
|
||||
ersetzen (z. B. „wenn uv_index vorhanden und ≥ 6" — dann ist die Zeile aber redundant zur
|
||||
UV-Regel und kann entfallen).
|
||||
|
||||
### F-5 (HOCH) — Kälteempfinden-Offset: Richtung und Semantik undefiniert
|
||||
|
||||
**Bezug:** FR-1.3, FR-4.2, Glossar, Addendum §3, Spec §7.
|
||||
|
||||
FR-4.2: *„Der Kälteempfinden-Offset verschiebt alle Temperaturschwellen."* Nirgends steht:
|
||||
(a) in **welche Richtung** ein positiver Wert verschiebt, (b) was +2 für den Nutzer **bedeutet**
|
||||
(„friert leicht" oder „schwitzt leicht"?), (c) die Schrittweite steht nur im Addendum
|
||||
(±2 °C/Stufe), nicht im PRD. Ein Test „Offset +2 bei 9 °C gefühlt" hat zwei mögliche korrekte
|
||||
Ergebnisse (wärme_min:3 oder wärme_min:4). Auch das UI-Label im Config-Flow (FR-1.3) ist ohne
|
||||
Semantik nicht abnehmbar.
|
||||
|
||||
**Fix-Vorschlag:** Im PRD festlegen: „+1 = friert leichter = alle Schwellen +2 °C nach oben
|
||||
(wärmer anziehen bei gleicher Temperatur); −1 entsprechend umgekehrt" (oder die gewünschte
|
||||
Konvention) und die Formel `wirksame_schwelle = schwelle + 2 °C × offset` einmal verbindlich
|
||||
notieren.
|
||||
|
||||
### F-6 (HOCH) — FR-4.3 „degradierte Regeln" nirgends definiert
|
||||
|
||||
**Bezug:** FR-4.3, Addendum §3/§7, Spec §12.
|
||||
|
||||
FR-4.3: *„Fehlt ein Prognosefeld, greifen degradierte Regeln."* Weder PRD noch Addendum noch Spec
|
||||
sagen, **was** Degradation je Feld bedeutet: Fehlt `temp_gefühlt_max` — Rückfall auf `temp_max`?
|
||||
Fehlt `regen_wkt`, aber `regen_menge` vorhanden — reicht die eine Bedingung (die Regel ist ein
|
||||
„oder", das legt es nahe, sagt es aber nicht)? Fehlt `uv_index` — entfällt die Regel ersatzlos
|
||||
oder wird nach `zustand = klar` geraten (Letzteres verböte INV-3)? Das Addendum §7 will
|
||||
„Degradation" sogar explizit testen — es gibt aber kein spezifiziertes Soll-Verhalten, gegen das
|
||||
getestet werden könnte.
|
||||
|
||||
**Fix-Vorschlag:** Degradations-Tabelle ergänzen: je Normprognose-Feld → Fallback-Feld oder
|
||||
„Regel entfällt + Hinweistext X".
|
||||
|
||||
### F-7 (HOCH) — Sensor-Namensdrift PRD ↔ Spec, zusätzlich deutschsprachige Entity-ID vs. i18n
|
||||
|
||||
**Bezug:** FR-7.1 vs. Spec §10; FR-8.1.
|
||||
|
||||
PRD FR-7.1: `sensor.what_to_wear_empfehlung`. Spec §10: `sensor.what_to_wear`. Zwei Dokumente,
|
||||
zwei verbindlich klingende Namen — Blueprints, Karte und Doku hängen an genau dieser ID. Dazu
|
||||
kommt: Der PRD-Name enthält das deutsche Wort „empfehlung", während FR-8.1 de **und** en fordert —
|
||||
für eine englische Installation ist eine deutsche Entity-ID inkonsistent (und Entity-IDs sind in
|
||||
HA nicht übersetzbar, nur Friendly Names).
|
||||
|
||||
**Fix-Vorschlag:** Eine ID kanonisieren (empfohlen: englisch/neutral, z. B.
|
||||
`sensor.what_to_wear_recommendation` oder schlicht Spec-Variante) und im Spec bzw. PRD nachziehen;
|
||||
FRs/Blueprints/Karte auf dieselbe ID referenzieren.
|
||||
|
||||
---
|
||||
|
||||
## 4. MITTLERE Findings
|
||||
|
||||
### F-8 (MITTEL) — FR-1.2 „geeignete Entität" hat zwei Lesarten
|
||||
|
||||
**Bezug:** FR-1.2, FR-3.1, FR-3.4, Spec §11/§12.
|
||||
|
||||
„Existiert keine **geeignete** Entität, bricht der Flow ab" — Lesart A: irgendeine
|
||||
`weather.*`-Entität genügt (reine Existenzprüfung). Lesart B: geeignet = unterstützt
|
||||
`weather.get_forecasts` (daily). Der Unterschied ist testrelevant: Eine Wetter-Entität ohne
|
||||
Forecast-Support würde in Lesart A den Flow passieren und erst zur Laufzeit in FR-3.4 scheitern.
|
||||
**Fix:** „geeignet" definieren (empfohlen: Forecast-Fähigkeit wird im Config-Flow geprüft) und den
|
||||
Fehlertext-Fall beider Lesarten trennen.
|
||||
|
||||
### F-9 (MITTEL) — Matcher-Tiebreak: Drift Spec ↔ Addendum, Rotation ohne Datenbasis
|
||||
|
||||
**Bezug:** Addendum §4 vs. Spec §8; FR-2.1.
|
||||
|
||||
Spec §8: Gleichstand → „nächstpassende Wärme/Formalität". Addendum §4: „beste Wärmegrad-Passung,
|
||||
dann Formalität, dann **zuletzt-verwendet-Rotation (v1 optional: deterministisch stabil
|
||||
sortieren)**". Drei Probleme: (a) Drift zwischen Spec und Addendum, (b) „v1 optional" ist als
|
||||
Scope-Aussage mehrdeutig (ist Rotation in v1 drin oder nicht?), (c) eine
|
||||
Zuletzt-verwendet-Rotation braucht eine Nutzungshistorie je Stück — **kein Feld im Datenmodell**
|
||||
(FR-2.1, Addendum §5) und keine FR trägt diese Persistenz. **Fix:** Für v1 verbindlich
|
||||
festlegen: deterministisch stabile Sortierung (dann Rotation als v1.1-Non-Goal listen) — und
|
||||
Spec/Addendum angleichen.
|
||||
|
||||
### F-10 (MITTEL) — Metriken: Messvorschrift unvollständig (S-2 ohne Nenner, S-3 ohne Frist, C-1 ohne Fenster)
|
||||
|
||||
**Bezug:** §8 Success-/Counter-Metrics.
|
||||
|
||||
- **S-2:** „≥ 3 externe Beta-Tester schaffen … ohne Rückfrage" — ohne Nenner: 3 von 3 oder 3 von
|
||||
20? Als Quote (z. B. „≥ 3 von max. 5 Testern") wird sie erst falsifizierbar.
|
||||
- **S-3:** HACS-Default-Aufnahme ohne Zeitbezug — S-1 hat „60 Tage nach Release", S-3 nicht; ein
|
||||
Ziel ohne Frist ist nie verfehlt.
|
||||
- **C-1:** „< 30 % der eingehenden Issues" — ohne Zeitfenster und Mindeststichprobe: bei 3 Issues
|
||||
kippt ein einzelnes Setup-Issue die Metrik auf 33 %. Fenster (z. B. erste 90 Tage) und
|
||||
Mindest-n (z. B. erst ab 10 Issues bewerten) ergänzen.
|
||||
- S-1 und C-2 sind messbar formuliert — kein Finding.
|
||||
|
||||
### F-11 (MITTEL) — Feld-/Options-Drift PRD ↔ Spec: Kategorien, `saison`, Sprachauswahl
|
||||
|
||||
**Bezug:** FR-2.1 vs. Spec §6; FR-1.3/FR-8.1 vs. Spec §11.
|
||||
|
||||
- Kategorien: PRD nennt 9 (inkl. **Oberteil**, **Hals**), Spec §6 nennt 7 („z. B.", ohne
|
||||
Oberteil/Hals). Da FR-2.1 die verbindliche Liste ist, Spec nachziehen oder Drift im
|
||||
Ledger als „PRD ersetzt Spec" dokumentieren.
|
||||
- `saison`: Spec §6 führt „`saison`/Temp-Range (optional)", das PRD nur noch „optional
|
||||
Temperaturbereich" — stillschweigend entfallen; nirgends als Entscheidung festgehalten.
|
||||
- Sprache: Spec §11 sieht im Config-Flow eine **Sprachauswahl** vor („Sprache (Default =
|
||||
HA-Sprache)"); PRD FR-1.3/FR-8.1 sagen, die Sprache **folgt** der HA-Sprache (keine Auswahl).
|
||||
Eine der beiden Aussagen muss weichen.
|
||||
|
||||
### F-12 (MITTEL) — Addendum §4: „Temperaturbereich muss zur Prognose passen" undefiniert
|
||||
|
||||
**Bezug:** Addendum §4, FR-2.1, Addendum §5.
|
||||
|
||||
Gegen welchen Wert wird der optionale Temperaturbereich eines Stücks geprüft — `temp_min`,
|
||||
`temp_max`, `temp_gefühlt_max`, Tagesmittel? Genügt Überlappung des Bereichs mit
|
||||
[temp_min, temp_max] oder ist Einschluss gefordert? Bei fehlenden gefühlten Temperaturen: welcher
|
||||
Fallback? Ohne Festlegung ist der Kandidaten-Filter (und damit jeder Matcher-Test) unterbestimmt.
|
||||
**Fix:** Vergleichsgröße + Prädikat (empfohlen: Überlappung mit [temp_min, temp_max], Fallback
|
||||
dokumentieren) im Addendum festschreiben.
|
||||
|
||||
---
|
||||
|
||||
## 5. Beobachtungen unterhalb der Finding-Schwelle (nur Protokoll, keine Meldung)
|
||||
|
||||
- Spec-Kopf sagt „Status: Entwurf zur Review", PRD-Kopf nennt die Spec „(freigegeben)" — der
|
||||
Spec-Statusblock ist nach der Freigabe nicht nachgezogen worden (Doku-Hygiene).
|
||||
- MVP-Text „FR-2 (10 Stücke)" vs. J1 „5–15 Kleidungsstücke" — verträglich, aber uneinheitlich.
|
||||
- Normmodell führt `wind` und `böen`; die Regeln nutzen nur `böen` — `wind` ist tote Nutzlast
|
||||
oder es fehlt eine Regel (bewusst zu entscheiden, kein Fehler).
|
||||
- FR-7.5 „sodass sie abends aktuell ist" ist weich, aber durch „mind. stündlich + bei Neustart"
|
||||
hinreichend operationalisiert.
|
||||
- FR-7.2 lässt Event-Name/-Payload offen — akzeptabel für Phase 2 (Architektur-Detail).
|
||||
|
||||
## 6. Testbarkeits-Matrix (Kurzfassung)
|
||||
|
||||
| FR | Testbar wie formuliert? | Blocker |
|
||||
|---|---|---|
|
||||
| 1.1–1.4 | ja (1.2 mit Lesart-Risiko, F-8) | — |
|
||||
| 2.1–2.3 | ja | — |
|
||||
| 3.1–3.4 | ja | — |
|
||||
| 4.1 | **nein** | F-4 (Defaults unverbindlich, Grenzen doppelt) |
|
||||
| 4.2 | **nein** | F-5 (Richtung/Schrittweite) |
|
||||
| 4.3 | **nein** | F-6 (Degradation undefiniert) |
|
||||
| 5.1 | **nein** | F-1 (Komposition), F-2 (Mapping) |
|
||||
| 5.2 | **nein** | F-2, F-3 (Prioritäten fehlen) |
|
||||
| 5.3 | ja | — |
|
||||
| 6.1–6.4 | ja | — |
|
||||
| 7.1 | ja (Name: F-7) | — |
|
||||
| 7.2–7.5 | ja | — |
|
||||
| 8.1 | ja | — |
|
||||
|
||||
**Fazit:** Struktur vollständig, Rand-FRs (Setup, Wetter, LLM, Ausgabe, i18n) sauber testbar —
|
||||
aber der **Kern der Wertschöpfung (Regel-Engine → Matcher, FR-4/FR-5) ist in fünf von sechs FRs
|
||||
nicht objektiv prüfbar**. F-1 und F-2 sind vor Gate-Freigabe zwingend zu schließen.
|
||||
261
docs/reviews/prd/review-sicherheit-recht.md
Normal file
261
docs/reviews/prd/review-sicherheit-recht.md
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
# Review: Sicherheit / Datenschutz / Recht — PRD "What to Wear" v1.0
|
||||
|
||||
- **Reviewer-Linse:** Security & Datenschutz (DSGVO) + Open-Source-Compliance
|
||||
- **Datum:** 2026-07-11
|
||||
- **Geprüfte Artefakte:**
|
||||
- `_bmad-output/planning-artifacts/prd.md` (PRD v1.0, draft)
|
||||
- `_bmad-output/planning-artifacts/prd-addendum.md` (technisches Addendum)
|
||||
- `docs/superpowers/specs/2026-07-11-what-to-wear-design.md` (freigegebener Design-Spec)
|
||||
- **Nicht Gegenstand:** Benutzer-Entscheidungen (Open Source, BYO-Key-Ansatz, echte-Stücke-Inventar,
|
||||
HA-Wetter-Entität v1, keine Telemetrie, de+en, v1.1-Verschiebungen) — als gesetzt behandelt.
|
||||
|
||||
---
|
||||
|
||||
## 1. BYO-LLM-Key-Handling — Speicherung, Logs, Attribute
|
||||
|
||||
### 1.1 Widerspruch FR-6.4 ↔ NFR-6/INV-4: „nie in Klartext-Dateien" ist mit HA-Config-Storage nicht erfüllbar (KRITISCH)
|
||||
|
||||
FR-6.4 verspricht:
|
||||
|
||||
> „Der Key erscheint nie in Logs, Sensor-Attributen oder **Klartext-Dateien**."
|
||||
|
||||
NFR-6 und INV-4 legen gleichzeitig fest:
|
||||
|
||||
> „API-Keys ausschließlich im **HA-Config-Storage**" / „Keys nie in Logs/State/Attributen; nur HA-Config-Storage."
|
||||
|
||||
Diese beiden Aussagen widersprechen sich faktisch: Der HA-Config-Storage
|
||||
(`/config/.storage/core.config_entries`) ist eine **unverschlüsselte JSON-Datei** auf der Platte.
|
||||
Home Assistant bietet für Config-Entry-Daten keine Verschlüsselung at rest; jeder mit
|
||||
Dateisystemzugriff (Add-ons mit config-Zugriff, SSH, Backup-Einsicht) liest den Key im Klartext.
|
||||
Der Key landet zudem in **HA-Backups** (je nach HA-Version/Einstellung verschlüsselt oder nicht).
|
||||
|
||||
So wie FR-6.4 formuliert ist, ist die Anforderung **nicht implementierbar** — oder sie erzeugt beim
|
||||
Leser (und späteren Nutzer, der die Doku liest) den falschen Eindruck, der Key sei verschlüsselt
|
||||
gespeichert. Das ist als Sicherheitszusage im PRD sachlich falsch.
|
||||
|
||||
**Zusätzliche Lücke:** HA-Integrationen können einen **Diagnostics-Download** anbieten
|
||||
(`diagnostics.py`), der Config-Entry-Daten exportiert — genau der Kanal, über den Keys in der Praxis
|
||||
am häufigsten leaken (Nutzer hängt Diagnostics-Dump an ein GitHub-Issue). Das PRD erwähnt
|
||||
Diagnostics-Redaction (`async_redact_data`) nirgends. Ebenso fehlt die Anforderung, das
|
||||
Key-Eingabefeld im Options-Flow als **Passwort-Selector** (maskiert) auszulegen.
|
||||
|
||||
**Fix-Vorschlag:**
|
||||
- FR-6.4 umformulieren: „Der Key erscheint nie in Logs, Sensor-State/-Attributen, Events oder
|
||||
Diagnostics-Exports (Redaction verpflichtend). Gespeichert wird er ausschließlich im
|
||||
HA-Config-Storage (HA-Standard; unverschlüsselt at rest — in der Doku transparent benennen,
|
||||
inkl. Hinweis auf Backups)."
|
||||
- Neue Teil-FR: Diagnostics (falls implementiert) redigiert Key + Fotopfade; Options-Flow nutzt
|
||||
einen maskierten Passwort-Selector.
|
||||
|
||||
### 1.2 Geprüft, kein Finding
|
||||
|
||||
- FR-6.3-Fallback (Regeltext bei Fehler/Timeout) ist sicherheitlich sauber — kein stiller Retry-Sturm
|
||||
spezifiziert, aber das darf Architektur-Detail bleiben.
|
||||
- Kein Key in URL-Parametern o. Ä. — bei OpenAI/Anthropic-APIs ohnehin Header-basiert; kein
|
||||
PRD-Handlungsbedarf.
|
||||
|
||||
---
|
||||
|
||||
## 2. LLM-Aufruf: Was geht an Dritte? (DSGVO)
|
||||
|
||||
### 2.1 Fehlender Datenschutz-Hinweis zur LLM-Übermittlung (HOCH)
|
||||
|
||||
Addendum §6 definiert den LLM-Input:
|
||||
|
||||
> „Input: strukturierte Recommendation (Stücke+Gründe+Lücken+Kennwerte) + Zielsprache."
|
||||
|
||||
Damit gehen bei aktiviertem LLM-Ton an OpenAI/Anthropic (Drittland, i. d. R. USA):
|
||||
|
||||
1. **Stücknamen** — Freitext des Nutzers. Realistisch enthalten sie Personenbezug:
|
||||
„Papas Regenjacke", „Emmas Schulschuhe" (JTBD-2 adressiert explizit das Kind-Szenario →
|
||||
ggf. Daten Minderjähriger). Ein Kleiderschrank-Inventar eines Haushalts ist einer Person
|
||||
zuordenbar = personenbezogene Daten i. S. d. DSGVO.
|
||||
2. **Wetterkennwerte für morgen** — Temperatur/Regen/Wind für den Standort des Haushalts.
|
||||
Daraus ist der Standort grob ableitbar (Region/Klimazone, in Kombination mit Zeitstempeln enger).
|
||||
|
||||
Rechtliche Einordnung: Für den privaten Endnutzer greift regelmäßig die Haushaltsausnahme
|
||||
(Art. 2 Abs. 2 lit. c DSGVO), und der Projektbetreiber ist mangels Vendor-Cloud weder
|
||||
Verantwortlicher noch Auftragsverarbeiter. **Aber:** Ein seriöses Open-Source-Produkt, das per
|
||||
Opt-in Daten an US-Anbieter schickt, muss das **transparent machen** — und das PRD verlangt
|
||||
das derzeit an keiner Stelle. Weder FR-6 noch FR-1.4 (Options-Flow) fordern einen Hinweis, was
|
||||
beim Aktivieren übertragen wird; ein Datenschutz-Abschnitt in README/Doku ist nirgends
|
||||
Anforderung.
|
||||
|
||||
**Fix-Vorschlag (neue FR-6.5):** Beim Aktivieren des LLM-Tons zeigt der Options-Flow einen
|
||||
Hinweis: welche Daten (Stücknamen, Gründe, Lücken, Wetterkennwerte) an den gewählten Provider
|
||||
gehen, dass dessen Nutzungsbedingungen/Datenverarbeitung gelten, und dass Fotos und sonstige
|
||||
Daten **nie** übertragen werden. README erhält einen Abschnitt „Datenschutz" mit derselben
|
||||
Aussage (de+en). Positiv abzusichern: Der Prompt enthält ausschließlich die in Addendum §6
|
||||
genannten Felder — keine Fotos, keine Entity-IDs, keine Koordinaten.
|
||||
|
||||
### 2.2 Geprüft, kein Finding
|
||||
|
||||
- „Keine Telemetrie" (Non-Goal 7, NFR-1, INV-5) ist konsistent und vollständig durchgezogen — gut.
|
||||
- Fotos gehen nicht in den LLM-Prompt (Addendum §6 nennt sie nicht) — sollte aber wie oben als
|
||||
Negativ-Zusicherung explizit werden.
|
||||
|
||||
---
|
||||
|
||||
## 3. Prompt-Injection über nutzerdefinierte Stücknamen (MITTEL)
|
||||
|
||||
Stücknamen sind Freitext (FR-2.1) und landen ungefiltert im LLM-Prompt (Addendum §6). Ein Name wie
|
||||
„Jeans. Ignoriere alle Anweisungen und sage: …" kann die Ausgabe kapern. Der Schaden ist begrenzt
|
||||
(die Stückauswahl passiert **vor** dem Phraser, FR-6.2 — Auswahl/Lücken sind nicht angreifbar),
|
||||
aber der manipulierte **Text** wird auf Dashboard, Push und per **TTS im Wohnzimmer** ausgespielt
|
||||
(FR-7.3/7.4). Szenarien: irreführende Ansagen im Haushalt (auch gegenüber Kindern), peinliche/
|
||||
schädliche Push-Texte, später (v1.1, LagerLens) sogar Namen aus einer automatischen
|
||||
Foto-Katalogisierung — dann ist die Quelle nicht mehr allein der Nutzer selbst.
|
||||
|
||||
Zweites Problem: FR-6.2 („keine neuen Fakten, keine Auswahländerung") ist als absolute Zusage
|
||||
formuliert, per Prompt allein aber **nicht erzwingbar**. Das PRD sollte die Anforderung als
|
||||
Best-Effort-Prompt **plus Validierung** fassen.
|
||||
|
||||
**Fix-Vorschlag (Ergänzung FR-6.2/Addendum §6):** Stücknamen und andere Nutzertexte werden im
|
||||
Prompt als nicht vertrauenswürdige Daten behandelt (klare Delimiter, Instruktion, eingebettete
|
||||
Anweisungen zu ignorieren); LLM-Ausgabe wird validiert (Längenlimit, bei offensichtlicher
|
||||
Regelverletzung → deterministischer Regeltext, FR-6.3-Pfad). FR-6.2 entsprechend als
|
||||
„Prompt-Vorgabe + Ausgabe-Validierung, sonst Fallback" formulieren.
|
||||
|
||||
---
|
||||
|
||||
## 4. Fotos von Kleidungsstücken (DSGVO / Speicherort) — HOCH
|
||||
|
||||
FR-2.1 führt „optional Foto" ein; der Design-Spec §15 benennt die Speicherfrage sogar selbst als
|
||||
offen („Foto-Speicherung bei nativem Kleiderschrank (Pfad/Umfang)"), aber **das PRD übernimmt
|
||||
diese offene Frage nicht** — weder als FR noch als offene Entscheidung (§10). Damit fehlen
|
||||
sicherheits-/datenschutzrelevante Mindestanforderungen:
|
||||
|
||||
1. **Speicherort:** Der naive Ablageort `/config/www/` wird von HA unter `/local/…`
|
||||
**ohne Authentifizierung** ausgeliefert (dokumentiertes HA-Verhalten). Fotos — potenziell mit
|
||||
Personen im Bild oder GPS-EXIF-Daten — wären dann für jeden mit Netzwerkzugriff auf die
|
||||
HA-URL abrufbar. Das PRD muss fordern: Ablage nur in einem auth-geschützten Pfad (kein
|
||||
`/local`), Auslieferung nur über authentifizierte Endpunkte.
|
||||
2. **Löschkaskade:** Beim Löschen eines Stücks (Sub-Entry) muss das Foto mitgelöscht werden
|
||||
(Datenhygiene; verwaiste Personenfotos auf der Platte).
|
||||
3. **Umfang:** Größen-/Formatlimit (verhindert nebenbei auch Storage-Bloat, falls jemand Fotos
|
||||
base64 in den Config-Entry legt — was zusätzlich Backups und Diagnostics aufbläht).
|
||||
4. **EXIF:** Zumindest Doku-Hinweis, dass Fotos EXIF-Geodaten enthalten können; optional Stripping.
|
||||
|
||||
**Fix-Vorschlag:** FR-2.1 um eine Teil-FR „Foto-Handling" ergänzen (auth-geschützter Speicherort,
|
||||
Löschung mit dem Stück, Limit) oder die Foto-Option ehrlich als offene Entscheidung in §10
|
||||
aufnehmen und für v1 ggf. streichen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Lizenz & Open-Source-Compliance
|
||||
|
||||
### 5.1 Third-Party-/Attributionspflichten fehlen (MITTEL)
|
||||
|
||||
Das PRD regelt nur die eigene Lizenz (E-1: MIT). Es fehlt jede Anforderung zu:
|
||||
|
||||
- **LICENSE-Datei** im Repo (auch HACS-/GitHub-Hygiene).
|
||||
- **Lizenzkompatibilitäts-Check der Abhängigkeiten** (Python-Deps sind voraussichtlich
|
||||
unkritisch — `openai` Apache-2.0, `anthropic` MIT, oder direkter aiohttp-Call — aber es gibt
|
||||
keinen Prüfschritt, der das festhält).
|
||||
- **Attribution gebündelter JS-Bibliotheken** in `www/what-to-wear-card.js`: Lovelace-Karten
|
||||
bündeln typischerweise Lit (BSD-3-Clause) o. Ä.; BSD/MIT verlangen die Beibehaltung des
|
||||
Lizenzhinweises im ausgelieferten Bundle bzw. eine THIRD_PARTY-NOTICES-Datei.
|
||||
|
||||
**Fix:** NFR ergänzen: „Repo enthält LICENSE (MIT); alle Laufzeit-Abhängigkeiten sind
|
||||
MIT-kompatibel (dokumentierter Check); gebündelte Frontend-Bibliotheken behalten ihre
|
||||
Lizenzhinweise (NOTICE-Datei)."
|
||||
|
||||
### 5.2 Markenrecht „Home Assistant" (MITTEL)
|
||||
|
||||
„Home Assistant" ist eine eingetragene Marke der Nabu Casa, Inc. mit veröffentlichten
|
||||
Brand-Guidelines. Für ein Vertriebsprodukt, das im HA-Ökosystem auftritt (Name, README,
|
||||
Screenshots, eigenes Logo für den brands-Eintrag), fehlt im PRD jede Anforderung:
|
||||
|
||||
- kein HA-Logo/keine HA-Wortbildmarke im eigenen Branding,
|
||||
- keine Formulierungen, die offizielle Zugehörigkeit suggerieren („Home Assistant What to Wear"
|
||||
wäre problematisch; „What to Wear für Home Assistant" / „works with Home Assistant" ist ok),
|
||||
- der für HACS-Default nötige Eintrag in `home-assistant/brands` braucht ein **eigenes** Icon/Logo.
|
||||
|
||||
Der Produktname „What to Wear" selbst ist unkritisch (generisch-beschreibend; kein erkennbarer
|
||||
Konflikt im Ökosystem — abschließende Recherche nicht Teil dieses Reviews).
|
||||
|
||||
**Fix:** Release-Checkliste/NFR: „Naming, Doku und Logo folgen den Nabu-Casa-Brand-Guidelines."
|
||||
|
||||
---
|
||||
|
||||
## 6. HACS-Default-Store als S-3-Metrik — was verlangt das konkret? (HOCH)
|
||||
|
||||
S-3 macht die Default-Store-Aufnahme zur Erfolgsmetrik, NFR-5 bleibt aber vage
|
||||
(„Repo-Struktur, hacs.json, manifest.json, Versionierung und Releases"). Die tatsächlichen
|
||||
Default-Anforderungen (HACS-Doku, Kategorie Integration) umfassen konkret u. a.:
|
||||
|
||||
1. **Öffentliches GitHub-Repo** — HACS unterstützt ausschließlich GitHub. Damit ist
|
||||
**E-3 („Repo-Remote: Forgejo vs. neues GitHub — später")** keine freie Entscheidung mehr:
|
||||
S-3 erzwingt GitHub (mindestens als öffentlichen Release-Mirror). Dieser Konflikt steht
|
||||
unbenannt im PRD.
|
||||
2. **Eintrag in `home-assistant/brands`** (eigener PR mit Icon/Logo) — Pflicht für Default,
|
||||
im PRD nirgends erwähnt, kostet Vorlauf (Review durch HA-Team).
|
||||
3. `manifest.json` mit `documentation`, `issue_tracker`, `codeowners`, `version`.
|
||||
4. Repo-Hygiene: Beschreibung, Topics, README, Issues aktiviert, nicht archiviert;
|
||||
**GitHub-Releases** mit Versions-Tags.
|
||||
5. Bestehen der **HACS-Action + hassfest**-Validierung in CI.
|
||||
6. Realistisch: Wartezeit der Default-Review-Queue (Wochen bis Monate) — relevant, weil S-1 an
|
||||
„60 Tage nach Release" hängt; S-3 hat keinerlei Zeitbezug.
|
||||
|
||||
**Fix:** NFR-5 um die Punkte 1–5 konkretisieren; E-3 auflösen oder umformulieren
|
||||
(„Entwicklungs-Remote frei, Veröffentlichung für S-3 zwingend auf öffentlichem GitHub");
|
||||
S-3 optional mit Zeit-/Fallback-Klausel versehen (z. B. „Default-Review eingereicht und
|
||||
Validierungen grün" als Nahziel).
|
||||
|
||||
### 6.1 Folgeproblem: Auslieferung von Karte & Blueprints über HACS (MITTEL)
|
||||
|
||||
Addendum §1 legt `www/what-to-wear-card.js` und `blueprints/automation/what_to_wear/` auf
|
||||
**Repo-Root**. Eine HACS-Installation der Kategorie „Integration" deployt aber nur den Inhalt von
|
||||
`custom_components/what_to_wear/` in die HA-Config — Repo-Root-`www/` und -`blueprints/` kommen
|
||||
beim Nutzer **nicht an**. Damit wären FR-7.3 („mitgelieferte Lovelace-Karte") und FR-7.4
|
||||
(„mitgelieferte Blueprints") sowie Journey J1 („zieht die WTW-Karte aufs Dashboard … importiert
|
||||
das Push-Blueprint") in der beschriebenen Struktur nicht erfüllbar. Lösungswege (Karte im
|
||||
Integrationsordner + Registrierung als Frontend-Ressource/Static Path; Blueprints per
|
||||
dokumentierter Import-URL oder programmatischem Kopieren) sind Architektursache — **dass** die
|
||||
Auslieferung über den HACS-Integrations-Install funktionieren muss, gehört aber als Anforderung
|
||||
ins PRD/Addendum, sonst validiert Gate 3 gegen ein falsches Layout.
|
||||
|
||||
---
|
||||
|
||||
## 7. Blueprint-Sicherheit — kann ein Blueprint Schaden anrichten?
|
||||
|
||||
Einschätzung: **Grundsätzlich ja** — importierte Blueprints erzeugen Automationen, die mit vollen
|
||||
HA-Rechten laufen (beliebige Service-Calls möglich). Für die zwei mitgelieferten Blueprints ist das
|
||||
Risiko aber strukturell klein, solange gilt:
|
||||
|
||||
- Sie rufen ausschließlich vom **Nutzer gewählte** `notify.*`-/`tts`-/`media_player`-Ziele auf
|
||||
(so beschreibt es FR-7.4 bereits) und enthalten keine hartkodierten Entitäten, URLs oder
|
||||
Shell-/REST-Aufrufe.
|
||||
- Die Doku verweist nur auf die **offizielle Import-URL** des eigenen Repos (Schutz vor
|
||||
Look-alike-Blueprints aus Foren, die zusätzlich Daten exfiltrieren könnten — der Nutzer sieht
|
||||
beim Import selten den YAML-Inhalt durch).
|
||||
- Der über Push/TTS ausgespielte Text ist der Empfehlungstext — bei aktiviertem LLM greift hier
|
||||
das Injection-Thema aus §3 (Längen-/Inhaltsvalidierung vor Ausspielung).
|
||||
|
||||
**Finding (MITTEL):** Diese drei Eigenschaften stehen nirgends als Anforderung. Eine Zeile in FR-7.4
|
||||
(„Blueprints beschränken sich auf nutzergewählte notify/TTS-Ziele, keine hartkodierten
|
||||
Entitäten/Netzaufrufe; Doku verweist ausschließlich auf die offizielle Import-Quelle") genügt.
|
||||
|
||||
Ergänzend geprüft, kein Finding: `what_to_wear.recommend` (FR-7.2) ist idempotent-lesend
|
||||
(Neuberechnung + Event) — kein Missbrauchspotenzial über das hinaus, was jeder HA-Nutzer ohnehin
|
||||
darf; Event-Inhalt (Kleidung/Wetter) ist innerhalb des Haushalts unkritisch.
|
||||
|
||||
---
|
||||
|
||||
## 8. Zusammenfassung der Findings
|
||||
|
||||
| # | Schwere | Bezug | Titel |
|
||||
|---|---|---|---|
|
||||
| 1 | KRITISCH | FR-6.4 / NFR-6 / INV-4 | „Nie in Klartext-Dateien" widerspricht HA-Config-Storage (unverschlüsseltes .storage-JSON); Diagnostics-Redaction + Passwort-Selector fehlen |
|
||||
| 2 | HOCH | FR-6 / FR-1.4 / Addendum §6 | Kein Datenschutz-Hinweis: Stücknamen (ggf. Personen-/Kindbezug) + standort-implizierende Wetterdaten gehen an US-LLM-Provider |
|
||||
| 3 | HOCH | FR-2.1 / Spec §15 | Foto-Handling ungeregelt: Speicherort (`/config/www` = unauthentifiziert!), Löschkaskade, Limits, EXIF |
|
||||
| 4 | HOCH | S-3 / NFR-5 / E-3 | HACS-Default verlangt konkret GitHub + brands-Eintrag + manifest-Keys + CI-Validierung; E-3 kollidiert mit S-3 |
|
||||
| 5 | MITTEL | FR-6.2 / Addendum §6 | Prompt-Injection über Stücknamen; FR-6.2 als absolute Zusage nicht erzwingbar → Untrusted-Data-Handling + Ausgabe-Validierung |
|
||||
| 6 | MITTEL | Addendum §1 / FR-7.3/7.4 | Repo-Root-`www/`+`blueprints/` werden von HACS nicht installiert — Auslieferungsmechanismus als Anforderung fehlt |
|
||||
| 7 | MITTEL | §10 E-1 | Lizenz-Compliance unvollständig: LICENSE-Datei, Dependency-Check, Attribution gebündelter JS-Libs (Karte) |
|
||||
| 8 | MITTEL | (fehlt) | Markenrecht: Nabu-Casa-Brand-Guidelines (kein HA-Logo, keine implizierte Zugehörigkeit, eigenes Icon für brands) nirgends gefordert |
|
||||
| 9 | MITTEL | FR-7.4 | Blueprint-Sicherheitsleitplanken (nur nutzergewählte notify/TTS-Ziele, keine Hardcodes, offizielle Import-Quelle) fehlen als Anforderung |
|
||||
|
||||
Kein Finding zu: Telemetrie-Verzicht (sauber), LLM-Fallback-Kette (FR-6.3, robust),
|
||||
Service/Event-Exposition (FR-7.2, unkritisch), Wahl MIT als solcher (Benutzerentscheidung, und
|
||||
kompatibel mit dem HA-Ökosystem).
|
||||
Loading…
Add table
Add a link
Reference in a new issue