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:
Nora 2026-07-11 21:09:12 +00:00
parent 4d7ef85406
commit a6d5635236
256 changed files with 31788 additions and 1 deletions

View 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 1522 °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. 515 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 13 min) → Config-Flow → **515 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 6090 s pro Stück für jemanden, der die Felder zum ersten Mal sieht. 10 Stücke ≈ 1015 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 34 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 510 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.

View 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 „08 °C", „815 °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
„1522 → 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 69 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 2448 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:
> „| 08 °C | wärme_min:4 | / | 815 °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 15, 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 2030 °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).

View 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,
J1J4), 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 „08" als
auch in „815"** (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 „515 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.11.4 | ja (1.2 mit Lesart-Risiko, F-8) | — |
| 2.12.3 | ja | — |
| 3.13.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.16.4 | ja | — |
| 7.1 | ja (Name: F-7) | — |
| 7.27.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.

View 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 15 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).