Story 4.2 (TDD, reiner Kern, Suite gruen 157/157): - logic/phraser.py: build_prompt (nur strukturierte Empfehlung, JSON-escaped Untrusted-Stuecknamen + Ignorier-Instruktion, nie Fotos/Koordinaten/Entity-IDs), validate (a-e: kein Markup/Link/Code/Steuerzeichen, <=700, jeder Stueckname+Luecke, >=20; Verstoss->None=Regeltext), Injection-Katalog (AD-16/FR-6.6). - luna-pro-Security-Review: 4/5 Findings uebernommen, 1 verworfen (Evidenz im Ledger). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
338 lines
28 KiB
Markdown
338 lines
28 KiB
Markdown
# Fortschritts-Ledger — What to Wear (WTW)
|
||
|
||
> Methode: **AI-Dev-Method** (BMAD + adversariale Reviewer-Linsen + KI-Zweitkritiker qwen/Cloud).
|
||
> Quelle der Methode: `/root/.claude/method/DEV-METHOD.md` (v1.1) bzw. Repo `Kenearos/ai-dev-method`.
|
||
> Dieses Ledger überlebt Kontext-Kompaktierung: erledigte Stories, offene Punkte, Gate-Ergebnisse
|
||
> (inkl. je Kritiker-Aufruf geschickter Dateien + Modell), Finding-Urteile.
|
||
|
||
**Stand:** 2026-07-13 · **Phase:** 5 LAUFEND — Story-Loop; **EPIC 1+2 KOMPLETT** (Stories 1.1–2.5, 130 Tests grün): 15-Minuten-Wow-Pfad steht (Kleiderschrank→Karte→Service→Push/TTS). Umgebung `.venv` Py3.13 + HA 2025.3.4. · Nächster Epic: 3 (Anpassung & Robustheit), Story 3.1
|
||
|
||
## Benutzer-Entscheidungen E-1…E-4 (2026-07-11)
|
||
- **E-1 Lizenz: Apache-2.0** (bewusst statt MIT-Vorschlag).
|
||
- **E-2 Min-HA: ≥ 2025.3** (Subentries; Architekturphase verifiziert gegen reale APIs).
|
||
- **E-3 Repo-Modell: Forgejo = Entwicklungs-Quelle, öffentliches GitHub = gepflegter
|
||
Push-Mirror + Vertriebskanal** (Releases/Issues auf GitHub — HACS-Pflicht). Kostet
|
||
Release-Disziplin; vor Release einrichten.
|
||
- **E-4 Foto-Feld: v1 gestrichen → v1.1** (LagerLens-Connector).
|
||
|
||
---
|
||
|
||
## Umgebung (für spätere Sessions wichtig)
|
||
- Bash läuft als **`openclaw`** (uid 1000), **kein passwortloses sudo**. `/root/Labor` gehört `root`;
|
||
Projektordner `/root/Labor/what-to-wear` wurde einmalig per sudo an `openclaw` übereignet.
|
||
- **qwen-Kritiker** nur über Tailscale erreichbar, wenn der Windows-Desktop (100.64.0.2) an ist
|
||
(am 2026-07-11 erreichbar). Fällt er aus → Cloud-Eskalation `openai/gpt-5.6-luna-pro` via OpenRouter
|
||
(`QWEN_MAXTOK=16000 QWEN_REASONING=medium` Pflicht). Tool: `/root/.claude/tools/qwen.mjs`.
|
||
- Repo: **lokal** (git `master`), noch kein Remote. Ziel-Remote später zu entscheiden (Forgejo vs. neues GitHub).
|
||
|
||
## Phase 0 — Intent
|
||
**Produkt:** Quelloffene **Home-Assistant-Integration** (`what_to_wear`, HACS), die abends sagt, welche
|
||
**konkreten Kleidungsstücke aus dem Kleiderschrank** man für morgen rauslegen soll — deterministisch aus
|
||
der Wetterprognose, optional sprachlich schön formuliert, ausgespielt über Dashboard/Push/Ansage.
|
||
**Vertriebsprodukt** (läuft in jeder Kunden-HA, nicht bei uns).
|
||
|
||
## Design-Entscheidungen (Phase 0/Brainstorm, vom Benutzer freigegeben)
|
||
1. Form: Open-Source **HACS-Custom-Integration** `what_to_wear`, standalone in jeder Kunden-HA. Kein Vendor-Cloud.
|
||
2. Monetarisierung: **kostenlos/Open-Source** (Reichweite/Reputation, späterer Upsell via LagerLens).
|
||
3. Kleiderschrank: **Inventar echter Stücke** (nicht nur Kategorien), v1 nativ über **HA-Sub-Entries**.
|
||
4. LagerLens: **Vision C** — Standalone-Kern + **optionaler** LagerLens-Connector (v1.1), nicht Pflicht.
|
||
5. Logik: **Hybrid** — Regeln bestimmen *was*, optionaler **LLM** (Bring-your-own-Key, default AUS) den *Ton*.
|
||
6. Wetterquelle v1: **HA-Wetter-Entität** via `weather.get_forecasts`; Open-Meteo als Zweit-Provider v1.1.
|
||
7. Ausgabe: **Sensor + Service/Event**, read-only **Lovelace-Karte**, **2 Blueprints** (Push, TTS) — alle 3 Kanäle.
|
||
8. Sprache: **de + en**.
|
||
9. Name: **What to Wear (WTW)**, Domain `what_to_wear`.
|
||
|
||
## Kritiker-Konsultationen
|
||
### K1 — Wetterquelle (Design-Entscheidung), 2026-07-11
|
||
- **Modell:** `openai/gpt-5.6-luna-pro` (Cloud-Eskalation; vom Benutzer ausdrücklich erlaubt, da nuancierte
|
||
HA-Architekturfrage). **Geschickte Datei:** `scratchpad/wetterquelle-briefing.md`. Laufzeit 43 s, 10 Findings.
|
||
- **Mein Urteil:** alle 10 nach eigener Prüfung **berechtigt** (kein Fehlalarm; starkes Modell).
|
||
- **Kern-Findings → übernommen ins Design:**
|
||
- [KRITISCH] HA hat nicht zwingend eine Wetter-Entität → Config-Flow-Auswahl + klarer Fehlerzustand.
|
||
- [KRITISCH] `weather.get_forecasts` liefert kein einheitlich reiches Schema (`return_response=True`,
|
||
HA-Mindestversion; UV/gefühlt/Böen/Regen-Wkt. oft fehlend) → bewusste Normalisierung, „fehlend ≠ 0".
|
||
- [HOCH] Kein stiller Feld-Fallback zwischen Providern → Provider stabil pro Konfiguration.
|
||
- [HOCH] Open-Meteo nicht pauschal „kostenlos/kommerziell frei" + Koordinaten an Dritte → Opt-in +
|
||
Attribution + Coordinator/Timeout/Backoff (deshalb v1.1, nicht v1-Default).
|
||
- [HOCH] „Morgen" = lokales Kalenderdatum in `config.time_zone` (DST 23/25 h), nie `now+24h`.
|
||
- [HOCH] Einheiten mehr als °C/°F → festes SI-Internmodell, Werte nie aus Feldname/Größenordnung raten.
|
||
- [MITTEL] Provider-Interface = ok; automatisch-fehlertoleranter Open-Meteo-Zweig = Over-Engineering für v1.
|
||
- **Konsequenz:** v1 = nur HA-Wetter-Entität hinter kleinem `WeatherProvider`-Interface; Open-Meteo v1.1.
|
||
|
||
### K2 — Adversariale PRD-Validierung (4 Linsen, Multi-Agent-Workflow), 2026-07-11
|
||
- **Aufbau:** 4 parallele Reviewer-Linsen (rubrik / adversarial / edge-cases / sicherheit-recht,
|
||
Claude-Subagenten) auf PRD Rev. 1 + Addendum + Spec; semantisches Dedup; je Finding ein
|
||
Skeptiker-Agent mit Widerlegungsauftrag und Dokument-Evidenz. 36 Agenten, Detail-Reviews in
|
||
`docs/reviews/prd/review-*.md`.
|
||
- **Ergebnis:** 45 Roh-Findings → 31 nach Dedup → **28 CONFIRMED / 3 REFUTED / 0 PLAUSIBLE**.
|
||
- **Urteil:** Alle 28 bestätigten Findings in PRD Rev. 2 + Addendum Rev. 2 eingearbeitet
|
||
(Kernpunkte: Outfit-Komposition/Schichtsumme; Requirement→Stück-Mapping inkl. Streichung
|
||
`profilsohle`; Zieldatum-/Umschaltzeitpunkt-Logik statt Mitternachts-Kipp; Min-HA ≥ 2025.3
|
||
statt 2024.12 [faktisch falsch]; Karten-/Blueprint-Auslieferung HACS-konform; Degradations-
|
||
tabelle mit met.no-Realität; Prioritäten je Requirement; Offset-Semantik; kanonische
|
||
Entity-ID `sensor.what_to_wear`; Foto → v1.1 [E-4]; FR-6.5/6.6 Datenschutz + Injection;
|
||
Metriken messbar gemacht inkl. S-4 Retention; NFR-9 Lizenz / NFR-10 Migration;
|
||
single_config_entry; PRD als führend ggü. Spec deklariert).
|
||
- **3 Verwerfungen (mit Evidenz, wie von den Skeptikern belegt):**
|
||
1. „Wetter-Entität gelöscht/umbenannt undefiniert" — REFUTED: FR-3.4 + INV-1 decken den Fall
|
||
(Entität liefert keine Prognose → erklärender Fehlzustand).
|
||
2. „Markenrecht Home Assistant nicht adressiert" — REFUTED: NFR-5/S-3 schließen den
|
||
brands-Eintrag mit eigenem Icon ein; brands-Review erzwingt das.
|
||
3. „Blueprint-Sicherheitsleitplanken fehlen" — REFUTED: FR-7.4 (nur nutzergewählte Ziele) +
|
||
NFR-1/INV-5 legen die Leitplanken bereits fest.
|
||
|
||
### K3 — Architektur-API-Verifikation (4 Recherche-Agenten, 2026-07-11)
|
||
- **Auftrag E-2/NFR-4:** alle real genutzten HA-APIs gegen Core-Tag 2025.3.0 verifiziert (Subentries/
|
||
Config, weather.get_forecasts, Lovelace/HACS/Blueprints, Services/i18n/LLM/Testing) + Nachverifikation
|
||
(Subentry-Update-Listener, Coordinator-Shutdown/Lock). **Ergebnis: min-HA 2025.3 HALTBAR** (Subentry-
|
||
Reconfigure ab 2025.3 in nativer UI). Kernbefunde: get_forecasts liefert ANZEIGE-Einheiten; met.no nie
|
||
apparent_temperature (§3a-Fallback = Normalfall); kein strings.json für Custom-Integrationen;
|
||
SupportsResponse.OPTIONAL; API-Fallen 2025.3/2025.4/2025.8/2025.12/2026.2 dokumentiert.
|
||
Belege: `.memlog.md` (Einträge 14–39) im Architektur-Workspace.
|
||
|
||
### K4 — Adversariale Architektur-Validierung (6 Linsen, 2026-07-11)
|
||
- **Aufbau:** 5 Reviewer-Linsen (Rubrik / Web-Verifikation / Inkompatibilitäts-Angriff / Edge-Cases /
|
||
Sicherheit-Recht) + Input-Abgleich (122 Punkte), parallel als Claude-Subagenten auf Spine Rev. 1.
|
||
Detail-Reviews: `_bmad-output/planning-artifacts/architecture/architecture-what-to-wear-2026-07-11/reviews/`.
|
||
- **Ergebnis:** Web-Verify 0 Sachfehler; Inkompat 17 Löcher (5 KRITISCH), Edge-Cases 16 Findings
|
||
(1 KRITISCH), Rubrik 10, Sicherheit 7, Abgleich 7 FEHLT/12 teilweise. **Alle Findings selbst geprüft:
|
||
sämtlich berechtigt** → Spine Rev. 2: neue AD-19…AD-24 (kanonischer Payload, Textmodell,
|
||
Requirement-Registry, Item-Schema, Options-Schema, Parsing-Besitz) + Schärfungen AD-1…AD-18.
|
||
|
||
### Gate 2 — Architektur (2026-07-11)
|
||
- **Modell:** `openai/gpt-5.6-luna-pro` via OpenRouter — **Ersatz für qwen** (qwen offline: Desktop
|
||
pingt, Port 8088 zu; gleiche Lage wie Gate 1, dortige Benutzer-Freigabe »weiter mit luna-pro«).
|
||
Laufzeit 69 s. **Geschickte Datei:** `ARCHITECTURE-SPINE.md` (Rev. 2) — ein Aufruf.
|
||
- **NICHT-melden-Liste (wörtlich):** »E-1 Lizenz Apache-2.0; E-2 minimale HA-Version 2025.3;
|
||
E-3 Forgejo-Entwicklungsquelle + öffentlicher GitHub-Push-Mirror als Vertriebskanal; E-4 kein
|
||
Foto-Feld in v1; PRD-Non-Goals v1 (Open-Meteo, LagerLens-Connector, CRUD-Karte,
|
||
Mehrpersonen-Profile, Rotation, Export/Import, Aktivitätskontext, Wäschestatus, Telemetrie);
|
||
die im Dokument als [PRÄZISIERUNG] markierten, im Ledger dokumentierten PRD-Abweichungen.«
|
||
- **Ergebnis:** 9 Findings (3 KRITISCH / 5 HOCH / 1 MITTEL). Urteile (Evidenz im Memlog E42):
|
||
1. Zeitbudget 10+25>30 s ✔ übernommen (LLM-Timeout 18 s + Budget-Arithmetik in AD-6)
|
||
2. Service-Frische-Race ✔ **teilweise** — Serialisierung in den Coordinator verlegt (asyncio.Lock
|
||
um `_async_update_data`); Verifikation zeigte: `async_refresh` startet eigenen Lauf, Risiko war
|
||
Überschreiben durch parallelen Alt-Lauf, nicht Warten
|
||
3. `coordinator.data is None` beim Erstfehler ✔ übernommen (Fehler-Contract in AD-7)
|
||
4. Event-/Response-Limits ✔ übernommen (strukturelle Begrenztheit + to_event-Assert, AD-19)
|
||
5. Semantische LLM-Manipulation ✖ **verworfen** als Architekturänderung (Evidenz: FR-6.6 final
|
||
definiert die objektiven Kriterien; semantische Validierung nicht objektiv entscheidbar) —
|
||
als dokumentiertes Rest-Risiko in AD-16 + README aufgenommen
|
||
6. LLM-Response-Body ungedeckelt ✔ übernommen (64-KB-Cap, AD-6)
|
||
7. AD-10↔AD-12-Widerspruch ✖ **verworfen** (Evidenz: AD-10 verbietet dict-ITEM-Zugriff
|
||
`hass.data["lovelace"]["…"]`, AD-12 nutzt Attribut-Zugriff `.mode`/`.resources`);
|
||
Stabilitätsrisiko-Teil ✔ teilweise (Ressourcen-Test im latest-CI-Job, AD-18)
|
||
8. Private Subentry-Helper ✖ **verworfen** als Blocker (Evidenz: offizielles Dev-Doku-Muster +
|
||
Kompat-Kaskade AD-9 + CI-Matrix fängt Drift); Migrations-Robustheit ✔ übernommen (AD-8)
|
||
9. Naive datetimes ✔ übernommen (verwerfen statt Zeitzone raten, AD-24)
|
||
- **Status: Gate 2 BESTANDEN** (nach Einarbeitung; Spine `status: final`).
|
||
|
||
### Phase 4 — Epics & Stories + Party-Mode (2026-07-13)
|
||
- **Artefakt:** `_bmad-output/planning-artifacts/epics.md` — 5 Epics, 23 Stories, alle mit
|
||
Given/When/Then + FR-/AD-Referenzen. Reihenfolge vorwärts-abhängigkeitsfrei; »15-Min-Wow«
|
||
(S-2/JTBD-3) landet Ende Epic 2.
|
||
- **Party-Mode (Pflicht, 4 Linsen parallel: Architekt/Dev/PM/UX):** Draft (5 Epics/19 Stories) →
|
||
reorganisiert (5 Epics/23 Stories). Alle Findings selbst geprüft, sämtlich berechtigt:
|
||
- **PM [KRITISCH]:** Golden-Path über 3 Epics verstreut → Epic 2 = »15-Minuten-Wow«
|
||
(Kleiderschrank+Karte+Service+Push zusammengezogen).
|
||
- **Architekt [HOCH]:** Story 1.6/1.7 zu groß → gesplittet (1.6 ha_entity+Provider-Port /
|
||
1.7 Coordinator / 1.8 Config-Flow+Lebenszyklus / 1.9 Sensor+Stale); `changed`/Store-Owner →
|
||
Coordinator (1.7); AD-23-Options-Schema einmalig in 1.1/const.py; CI-grün von Mirror entkoppelt.
|
||
- **Dev [HOCH]:** RawForecast/NormForecast ins Modell (1.1); WeatherProvider-Protocol-AC (1.6);
|
||
`__init__`-Unload-Lebenszyklus (1.8); NFR-7-Timing-Assert (1.7).
|
||
- **UX [KRITISCH]:** `stale`-Feld nutzersichtbar (Payload 1.5 + Sensor-Owner 1.9 + Karten-Render 2.3);
|
||
Erklärbarkeit im Volltext (1.5); Doku-Links in Config-Flow-Fehlern (1.8); Ton-Indikator auf Karte (2.3).
|
||
- LLM-Phraser (rein) von LLM-Client (Adapter) getrennt (4.2/4.3).
|
||
- **Readiness-Check (adversarial):** **READY-MIT-AUFLAGEN** → alle 34 FRs / 10 NFRs / 24 ADs in
|
||
≥ 1 Story-AC; alle 10 Party-Mode-Fixes bestätigt. 3 Auflagen eingearbeitet: (1) 1.8/1.9-
|
||
Plattform-Naht (1.8 testet mit `PLATFORMS=[]`, 1.9 schaltet Forwarding scharf → keine
|
||
Forward-Dependency); (2) NFR-1 expliziter Netz-Abstinenz-Assert im 5.3-E2E; (3) 1.4-Matcher
|
||
emittiert sprachneutrale Grund-Keys, 1.5 lokalisiert (AD-13). **epics.md `status: final`.**
|
||
- **Test-Umgebung provisioniert:** `.venv` (Python 3.13.11) + `pytest-homeassistant-custom-component
|
||
==0.13.225` (HA **2025.3.4** = Min-Ziel) + pytest 8.3.4. aiohttp==3.11.13 ist yanked → expliziter
|
||
Pin nötig (für CI-Requirements merken). Bereit für den Story-Loop.
|
||
|
||
### Dokumentierte PRD-/Addendum-Präzisierungen (Architekturphase, FR-4.1-konform)
|
||
1. FR-7.1 »LLM-Text in State/Karte« → State trägt max 255 Zeichen ⇒ `llm_text` nur in
|
||
Event + Service-Response; Karte v1 = Kurztext + Strukturdaten (AD-20).
|
||
2. Addendum §5 »Morgenfenster vollständig abdecken« → ≥ 1 hourly-Eintrag im Fenster (AD-17).
|
||
3. Addendum §5 Tagesaggregate »aus daily« → bei fehlendem daily deterministisch aus hourly (AD-17).
|
||
4. Addendum §8 »englische Namen via Translations« → `logic/texts.py` (hassfest-Schema, AD-13).
|
||
5. FR-2.1-UI-Kategorien → englisches Enum + Selector-Translations (AD-22).
|
||
6. Statusmenge: effektiv leerer Schrank ist kein Fehlerstatus, sondern Empfehlungsinhalt (AD-2).
|
||
|
||
### Gate 1 — PRD (2026-07-11)
|
||
- **Modell:** `openai/gpt-5.6-luna-pro` via OpenRouter — **Ersatz für qwen** (qwen offline:
|
||
Desktop pingt, Port 8088 zu; Benutzer-Freigabe »weiter mit luna-pro«). Laufzeit 58 s.
|
||
- **Geschickte Dateien:** `_bmad-output/planning-artifacts/prd.md` (Rev. 2) +
|
||
`_bmad-output/planning-artifacts/prd-addendum.md` (Rev. 2) — ein Aufruf.
|
||
- **NICHT-melden-Liste (wörtlich, wie an den Kritiker gesendet):** »kostenlos/Open-Source ohne
|
||
Telemetrie; Kleiderschrank als Inventar echter Stücke; LLM-Ton optional per
|
||
Bring-your-own-Key (default aus); Wetterquelle v1 ausschließlich HA-Wetter-Entität;
|
||
LagerLens-Connector, Open-Meteo, CRUD-Karte, Mehrpersonen-Profile, Foto-Feld, Rotation,
|
||
Export/Import erst v1.1; Zielplattform Home Assistant; Sprachen de+en.«
|
||
- **Ergebnis:** 12 Findings (3 KRITISCH / 5 HOCH / 4 MITTEL). **Eigenes Urteil: alle 12
|
||
übernommen** (2 davon teilweise, s. u.). Fixes in PRD Rev. 3 + Addendum Rev. 3:
|
||
1. Umschaltzeitpunkt-Recompute exakt (FR-7.5) ✔ übernommen
|
||
2. Forecast-Zeitzuordnung/TZ + prognose_stand (Addendum §5, FR-7.1) ✔ **teilweise** — der
|
||
Teil »frisch berechnet, aber Provider-Forecast veraltet« ist über prognose_stand-Attribut
|
||
sichtbar gemacht, ein hartes Alters-Limit der Provider-Prognose bewusst nicht definiert
|
||
(Evidenz: HA liefert kein verlässliches Prognose-Erzeugungsdatum über get_forecasts;
|
||
Erkennbarkeit statt Blockade).
|
||
3. FR-6.6 objektive Abnahmekriterien + Injection-Testkatalog ✔ übernommen
|
||
4. Service-Response statt Event-Race (FR-7.2) ✔ übernommen
|
||
5. Test-Abruf: Timeout + 4. temporärer Fehlerfall (FR-1.2) ✔ übernommen
|
||
6. Subentry-Annahme ✔ **teilweise** — CI-Test gegen Minimalversion ergänzt (NFR-4);
|
||
Migration/Löschen bereits durch NFR-10/FR-2.4 abgedeckt (Evidenz: Zitate ebd.).
|
||
7. Zusammengesetzte Wärmebedarf-Lücke + Konfliktvorrang (Addendum §4.6) ✔ übernommen
|
||
8. Lovelace-Registrierung präzisiert: idempotent, ?v=, YAML-Erkennung, Deregistrierung ✔
|
||
9. Options-Struktur + Offset-Scope (nur °C-Schwellen) (FR-1.4/4.2) ✔ übernommen
|
||
10. Attribut-/Event-Größengrenzen (FR-7.1) ✔ übernommen
|
||
11. Sprachbindung dynamisch vs. registriert (FR-1.3) ✔ übernommen
|
||
12. Beispiel-Set normativ (Addendum §8 + Abnahmetest) ✔ übernommen
|
||
- **Status: Gate 1 BESTANDEN** (nach Einarbeitung; PRD `final` nach Benutzerfreigabe E-1…E-4).
|
||
|
||
## Gates (Status)
|
||
| Gate | Gegenstand | Modell | Datei(en) | Status |
|
||
|---|---|---|---|---|
|
||
| — | Wetterquelle (Design) | luna-pro | wetterquelle-briefing.md | ✅ verifiziert |
|
||
| — | PRD adversarial (4 Linsen) | Claude-Workflow | prd.md, prd-addendum.md, Spec | ✅ 28/31 übernommen |
|
||
| Gate 1 | PRD Rev. 2→3 | **luna-pro (qwen-Ersatz)** | prd.md + prd-addendum.md | ✅ bestanden, 12/12 übernommen |
|
||
| Gate 2 | Architektur Rev. 2 | **luna-pro (qwen-Ersatz)** | ARCHITECTURE-SPINE.md | ✅ bestanden, 9 Findings (6 übernommen, 3 verworfen mit Evidenz) |
|
||
| Gate 3 | Code (Security) | qwen/luna | — | offen |
|
||
|
||
**Architektur-Artefakt:** `_bmad-output/planning-artifacts/architecture/architecture-what-to-wear-2026-07-11/`
|
||
(`ARCHITECTURE-SPINE.md` final, 24 ADs; `.memlog.md` 43 Einträge; `reviews/` 6 Linsen). Paradigma:
|
||
Pipes-and-Filters-Kern (`logic/`, hass-frei) in Ports-and-Adapters-Schale.
|
||
|
||
## Stories (Phase 5 — Story-Loop; TDD, luna-pro-Review je Story, ein Commit je Story)
|
||
> qwen offline → Story-Reviews mit luna-pro (dokumentierter Ersatz). Umgebung: `.venv` Py3.13 + HA 2025.3.4.
|
||
|
||
- **1.1 Skeleton/Model/const/Guard** ✅ — 19 Tests grün. Dateien: `logic/model.py`, `const.py`,
|
||
`__init__.py`, `logic/__init__.py`, `manifest.json`, `hacs.json`, `tests/{test_layering,
|
||
test_const_schema, logic/test_model}.py`. **luna-pro-Review:** 6 Findings, 4 übernommen
|
||
(default_options()-Deepcopy F3; condition eigenes Feld F4; gaps_count/alert abgeleitet statt
|
||
gespeichert F5; Guard auf AST F6), 2 verworfen mit Evidenz (F1 RawForecast transient/nicht
|
||
signiert → keine Tief-Immutabilität nötig; F2 Schema-Validierung ist Story 3.1/3.2-Scope).
|
||
- **1.2 Forecast-Normalizer** ✅ — 31 Tests grün. `logic/normalize.py` + `tests/logic/test_normalize.py`.
|
||
**luna-pro-Review:** 8 Findings, 6 übernommen (u. a. **echter Bug F2:** UTC-Zeitstempel müssen vor
|
||
Morgenfenster-Auswertung nach `ZoneInfo(time_zone)` konvertiert werden; F3 Plausibilität pro Element
|
||
vor Aggregation; F4 naive datetimes verwerfen; F5 `_num`-Typguard; F6 kein bool als Messwert;
|
||
F8 condition-Fallback per Schwere-Max), 1 teils (F1 lightning-rainy→gewitter behalten, rules-korrekt +
|
||
dokumentiert), 1 verworfen mit Evidenz (F7 gemischte Per-Eintrag-Einheiten treten bei HA nicht auf).
|
||
- **1.3 Regel-Engine** ✅ — 47 Tests grün. `logic/rules.py` + `tests/logic/test_rules.py`. Doku-Fix:
|
||
Offset-Beispiel in epics.md korrigiert (Richtung war verkehrt: +1 = Schwellen +2 °C nach oben ⇒ bei
|
||
9 °C Wärmegrad 3→4, mehr Wärme). **luna-pro-Review:** 1 Finding **verworfen mit Evidenz** (Schnee +
|
||
temp fehlt → warmth:4 ist korrekt: §3a entfernt nur die Temperatur-Band-Regel, Schnee ist »übrige
|
||
Regel« mit Wärme-Floor; temp_missing-Note bleibt richtig).
|
||
|
||
## Offene Punkte / nächste Schritte
|
||
- Phase 4: Epics & Stories aus dem Spine + Party-Mode + Readiness-Check.
|
||
- Autonomer Durchlauf (Goal-Hook, Benutzer abwesend): qwen offline → Gates mit luna-pro (dokumentiert).
|
||
- Bei Phase 7: irreversible/identitätskritische Schritte (GitHub-Mirror-Setup E-3, brands-PR,
|
||
HACS-Default-Antrag, Version-Tag/Release) dem Benutzer vorlegen, nicht selbst ausführen.
|
||
- **1.4 Matcher** ✅ — 59 Tests grün (inkl. normativer §8-Frost/Regen-Abnahmetest). `logic/matcher.py`
|
||
+ `tests/logic/test_matcher.py`. **luna-pro-Review:** 7 Findings, 6 übernommen (F1 §4.6-Prioritäts-
|
||
konflikt Wärme↔Attribut; F2 Attribut auch auf Top/Pullover; F4 Wärme-Lücken bei fehlender
|
||
Hose/Schuhe/Top; F5 sturdy_shoes bevorzugt jede wasserdichte; F6 Tiebreak-Determinismus; F7
|
||
Lücken-Priorität aus Requirement), 1 verworfen mit Evidenz (F3 Schicht-Minimierung würde §4.2-
|
||
Reihenfolge + §8-Abnahmetest brechen).
|
||
- **1.5 Texte & Payload-Projektionen** ✅ — 72 Tests grün. `logic/texts.py`, `logic/assemble.py`,
|
||
Projektionen in `logic/model.py` + `tests/logic/test_texts_payload.py`. Volltext webt Gründe +
|
||
Datenlage ein; `to_payload`/state/attributes/event/response mit Shedding-Kaskade; `compute_signature`.
|
||
**luna-pro-Review:** 6 Findings, 5 übernommen (F1/F6 finaler Größen-Garant nach Shedding; F3
|
||
feels_like_min/max ins Payload; F4 Signatur über Item-Identität statt nur Namen; F5 texts._lang-Subtag),
|
||
1 verworfen mit Evidenz (F2 HA nutzt orjson-UTF-8 ohne \uXXXX → ensure_ascii=False misst korrekt + 1-KB-Puffer).
|
||
**→ Epic 1 Kern (logic/) komplett; als Nächstes HA-Adapter (1.6 Wetter-Adapter).**
|
||
- **1.6 Wetter-Adapter (Port + HA-Entität)** ✅ — 79 Tests grün (phcc). `weather/provider.py`
|
||
(Protocol), `weather/ha_entity.py`, `weather/errors.py` + `tests/conftest.py` +
|
||
`tests/test_weather_adapter.py`. get_forecasts (blocking+return_response), Feature-Check,
|
||
Zeit-Parsing/Verwerfen naiver, Einheiten aus State (AD-17/24). **luna-pro-Review:** 4 Findings,
|
||
alle übernommen (F1 Struktur-Guards [graceful-empty bewusst behalten], F2 Eintrags-Mapping-Guard,
|
||
F3 aware via utcoffset(), F4 int()-Guard).
|
||
- **1.7 Coordinator (einziger Mutator)** ✅ — 97 Tests grün. `coordinator.py`, `logic/pipeline.py`,
|
||
`logic/schedule.py` + `tests/{test_coordinator, logic/test_pipeline, logic/test_schedule}.py`.
|
||
Lock, Zieldatum/Umschaltzeit (halboffen), asyncio.timeout(10), Fehler-Contract, changed via Store,
|
||
last_success_utc, Item-Aufbau aus Subentries, Timing-Assert <5s (AD-2/3/7). **luna-pro-Review:**
|
||
6 Findings, 5 übernommen (F2+F3 einheitlicher Fehler-Contract für Abruf- UND no-coverage-Fehler;
|
||
F4 changed=stored!=sig; F5 _bool-Coercion; F6 parse_switchover tz strippen), 1 verworfen mit Evidenz
|
||
(F1 `datetime.time()` ist naiv, kein TypeError — Reviewer verwechselt mit `.timetz()`).
|
||
- **1.8 Config-Flow + Entry-Lebenszyklus** ✅ — 105 Tests grün (phcc). `config_flow.py`, `__init__.py`,
|
||
`translations/{en,de}.json`, `requirements-test.txt` + `tests/test_config_flow.py`. Test-Abruf mit
|
||
4 übersetzten Fehlern, single_config_entry, Setup/Unload/Listener leak-frei (PLATFORMS=[]), Doku-Links
|
||
in Meldungen. **Umgebungs-Fix:** `home-assistant-frontend==20250306.0` nötig (frontend-Dependency);
|
||
phcc-`hass_storage`-Fixture gegen Lingering-Timer. **luna-pro-Review:** 3 Findings, 2 übernommen
|
||
(F1 breitere Fehlerklassifikation→temporary; F2 Refresh auch bei entfernter Entität), 1 verworfen mit
|
||
Evidenz (F3 HA räumt on_unload bei Setup-Fehler selbst; PLATFORMS=[] kann nicht fehlschlagen).
|
||
- **1.9 Sensor** ✅ — 108 Tests grün (phcc). **EPIC 1 KOMPLETT.** `sensor.py`, `__init__.py`
|
||
(PLATFORMS=[SENSOR]), `translations/*` (entity-Name) + `tests/test_sensor.py`. Stabile ID
|
||
sensor.what_to_wear, Stale-Besitz (Einmal-Timer), leerer Schrank=ok, kein llm_text in Attributen
|
||
(AD-14/15/2). **luna-pro-Review:** 3 Findings, 2 übernommen (F2 1s-Timer-Margin; F3 _stale_fired
|
||
räumt _stale_unsub nicht mehr → kein Handle-Verlust bei Race), 1 verworfen mit Evidenz (F1 to_state()
|
||
schneidet bereits auf 255).
|
||
|
||
## Epic 2 — Der 15-Minuten-Wow
|
||
- **2.1 Kleidungsstücke verwalten (Sub-Entries)** ✅ — 114 Tests grün. `config_flow.py`
|
||
(ItemSubentryFlow user+reconfigure, Kompat-Accessor), `__init__.py`/`coordinator.py`
|
||
(Options-vs-Subentry-Listener via Snapshot), `translations/*` (config_subentries+selector).
|
||
**luna-pro-Review:** 4 Findings, 3 übernommen (F1 float-Guard in Validierung; F2 warmth round+clamp;
|
||
F3 Eingaben nach Fehler erhalten), 1 verworfen mit Evidenz (F4 Listener-Serialisierung — HA ruft
|
||
sequenziell, In-Flight-Refresh harmlos, seltene manuelle Aktion; v1-Vereinfachung).
|
||
- **2.2 Beispiel-Kleiderschrank** ✅ — 118 Tests grün (stabil ×5). `logic/texts.py` (example_items,
|
||
12 §8-Stücke lokalisiert, keine Handschuhe), `config_flow.py` (create_example-Option, subentries=),
|
||
`translations/*`, `tests/test_example_set.py` (inkl. §8-Frost/Regen-Abnahmetest mit echtem Set).
|
||
Test-Infra-Fix: conftest entlädt WTW-Entries nach jedem hass-Test (kein Coordinator-Timer-Flake).
|
||
**luna-pro-Review:** 2 Findings, beide übernommen (F1 create_example-Default-Fallback; F2 _lang/
|
||
pick_language auch Unterstrich-Locales de_DE).
|
||
- **2.3 Lovelace-Karte + Auto-Registrierung** ✅ — 122 Tests grün. `frontend.py`, `www/what-to-wear-card.js`,
|
||
`__init__.py` (async_setup_card + async_remove_entry) + `tests/test_frontend_card.py`. StaticPath +
|
||
Ressource idempotent (Storage-Mode, Attribut-Zugriff), Deregistrierung bei Remove; Karte XSS-sicher
|
||
(nur textContent, kein innerHTML), stale+tone-Indikator (AD-12). **luna-pro-Review:** 7 Findings,
|
||
6 übernommen (F1 customElements-Guard; F2+F4 asyncio.Lock gegen Registrierungs-Race; F5 Zieldatum+Label;
|
||
F6 Platzhalter nutzt this._entity; F7 null-Eintrags-Guards), 1 verworfen mit Evidenz (F3 manifest-
|
||
dependency garantiert lovelace-Verfügbarkeit).
|
||
- **2.4 Service recommend + Event** ✅ — 127 Tests grün. `services.py`, `services.yaml`,
|
||
`translations/*` (services), `__init__.py` + `tests/test_service.py`. SupportsResponse.OPTIONAL,
|
||
ungedrosselter async_refresh (Coordinator-Lock serialisiert), Event immer, stale-Stempelung,
|
||
Frische-/Parallel-Test (AD-11). **luna-pro-Review:** 4 Findings, 2 übernommen (F1 stale aus
|
||
last_success_utc stempeln; F4 eigene Registrierung per Flag), 2 verworfen mit Evidenz (F2 frühe
|
||
Returns unerreichbar da Service nur mit Coordinator registriert + data nie None; F3 async_refresh
|
||
propagiert keine Exceptions, to_event/response projizieren jeden Status).
|
||
- **2.5 Blueprints Push & TTS** ✅ — 130 Tests grün. **EPIC 2 KOMPLETT.**
|
||
`blueprints/automation/what_to_wear/{notify_push,announce_tts}.yaml` + `tests/test_blueprints.py`
|
||
(deklaratives Artefakt: Struktur-/YAML-Validierung statt rotem Test). min_version 2025.3.0,
|
||
device/time/boolean bzw. tts/media_player/trigger-Selektoren, what_to_wear.recommend via
|
||
response_variable, changed/gaps_count/alert-Bedingung (FR-7.4/AD-11/20). **luna-pro-Review:**
|
||
6 Findings, 5 übernommen (F1–F4 Template-Härtung mit .get()+Defaults; F5 continue_on_error am
|
||
notify), 1 verworfen mit Evidenz (F6 to_response ist flach, kein Nesting).
|
||
|
||
## Epic 3 — Anpassung & Robustheit
|
||
- **3.1 Options-Flow** ✅ — 135 Tests grün. `config_flow.py` (WhatToWearOptionsFlow, async_get_options_flow,
|
||
async_test_entity-Refactor mit switchover-Param), `coordinator.py`/`__init__.py` (data-Snapshot →
|
||
Reload auch bei Entity-Wechsel), `translations/*` (options+abort). Schwellen einzeln, Bandgrenzen
|
||
strikt monoton, TimeSelector HH:MM:SS, Entity-Wechsel mit Test-Abruf (AD-23/8/10). **luna-pro-Review:**
|
||
7 Findings, alle 7 übernommen (F1+F7 atomares async_update_entry+abort → ein Reload; F2 switchover an
|
||
Test-Abruf; F3 HH:MM:SS-Normalisierung; F4 try um Konvertierung; F5 alle Schwellen typ-normalisiert;
|
||
F6 Options aus current-Kopie → LLM-Block bewahrt).
|
||
- **3.2 Schema-Versionierung & Migration** ✅ — 141 Tests grün. `__init__.py` (async_migrate_entry
|
||
v1→v1 no-op), `const.py` (sanitized_options: fehlend→Default, typfalsch→Default+Warn, unbekannt→
|
||
erhalten, nie Abbruch; Bereichs-/NaN-/HH:MM:SS-Prüfung), `coordinator.py` (robustes _options) +
|
||
`tests/test_migration.py`. **luna-pro-Review:** 5 Findings, 4 übernommen (F1 Switchover-Listener
|
||
nutzt Sanitizer; F2 Bereichs-/isfinite-Prüfung; F3 HH:MM:SS-Validierung; F4 non-Mapping-Guard),
|
||
1 verworfen mit Evidenz (F5 VERSION/MINOR_VERSION sind in config_flow.py gesetzt). **Eigener Fund
|
||
beim Verifizieren:** F4-Guard `isinstance(dict)` verwarf `MappingProxyType` (entry.options) → auf
|
||
`Mapping` korrigiert (Test fing es).
|
||
|
||
## Epic 4 — Optionaler LLM-Ton
|
||
- **4.1 LLM-Optionen & Datenschutz-Disclosure** ✅ — 148 Tests grün. `config_flow.py` (LLM-Sektion im
|
||
Options-Flow: Passwort-Selector, leer=behalten, Deaktivieren=löschen, Provider-Whitelist),
|
||
`translations/*` (Disclosure de+en + Labels) + `tests/test_llm_options.py` (AD-5/FR-6.1/6.5).
|
||
**luna-pro-Review (Security):** 4 Findings, 3 übernommen (F1 Key .strip() gegen Whitespace-Overwrite;
|
||
F2 Deaktivieren löscht Key; F3 Provider-Whitelist serverseitig), 1 verworfen mit Evidenz (F4 Disclosure
|
||
liegt in Übersetzungen options.step.init.description, per Test verifiziert).
|
||
- **4.2 LLM-Phraser & Injection-Katalog (rein)** ✅ — 157 Tests grün. `logic/phraser.py` +
|
||
`tests/logic/test_phraser.py`. build_prompt (nur strukturierte Daten, JSON-escaped Untrusted-Namen,
|
||
Ignorier-Instruktion), validate (a–e, fail-closed→Regeltext), Injection-Katalog (AD-16/FR-6.6).
|
||
**luna-pro-Security-Review:** 5 Findings, 4 übernommen (F1 full_text-summary entfernt; F2 strengere
|
||
URL/Scheme/Entity-Sperre; F3 Steuerzeichen+Markdown verboten; F4 leere Namen übersprungen),
|
||
1 verworfen mit Evidenz (F5 items+gaps-beide-leer unerreichbar bei status=ok).
|