## Verdikt: BLOCKIERT **P0: 0 · P1: 2 · P2: 1** ### P1 1. **API-Response-Modelle sind nicht strikt allowlist-basiert** - `src/jarvis_finance/api/schemas/performance_activation.py:268-273,303-318` - Mehrere Felder verwenden `dict[str, Any]` bzw. `list[dict[str, Any]]`. Der reale `model-preview` liefert dadurch unter anderem Positions-IDs, ISIN, Instrument-/Providerdaten, Preis-IDs, Source-Referenzen, Cashflows und vollständige Provenienz aus. - `StrictModel(extra="forbid")` schützt nur die oberste Ebene; die verschachtelten Objekte bleiben laut OpenAPI `additionalProperties: true`. - **Auswirkung:** Der öffentliche Response-Vertrag ist nicht geschlossen und kann unbeabsichtigt zusätzliche finanzielle Detail- oder Herkunftsdaten offenlegen. - **Fix:** Eigene verschachtelte `StrictModel`-Klassen für Anchor, Position-Zusammenfassung, letzten Wert, Performance-Preview und Activation-Package definieren. Nur tatsächlich vom UI benötigte Felder ausgeben; Rohpositionen und Provenienz nicht über den UI-Endpunkt exponieren. 2. **Bestätigter Wert wird im UI als modellierter Verlaufskontext dargestellt** - `src/jarvis_finance/services/truewealth_valuation.py:236-252` - `frontend/src/components/performance/PortfolioPerformancePanel.vue:171-172` - Wenn noch nie ein modellierter Wert erfolgreich war, setzt der Service `latest_successful_value` auf einen offiziellen bestätigten Snapshot. Das UI zeigt diesen anschließend innerhalb der Karte **„Modellierter Wert“** als „Kein neuer Tageswert · zuletzt …“. - Der deterministische Gegenprobe ergab: `status=blocked`, `modelled_value_chf=None`, aber `latest_successful_value.source=truewealth_official_import`. Genau diese Doppelbelegung ist auch im 1440-UAT sichtbar: bestätigter und vermeintlich letzter modellierter Wert sind identisch. - **Auswirkung:** Bestätigt-/modelliert-Semantik ist für Nutzer materiell missverständlich. - **Fix:** `latest_modelled_value` ausschließlich aus `MODEL_SOURCE` beziehen und bei fehlendem Modellwert „Noch kein modellierter Tageswert“ anzeigen. Offizielle Fallback-Werte nur in der bestätigten Karte darstellen. ### P2 1. **44×44-UAT deckt nicht alle semantischen Controls ab** - `/tmp/sprint20d_browser_uat.py:62-85` - Der Probe selektiert nur `button,select,input`, prüft ausschließlich die **Höhe** und lässt die Einrichtung geschlossen. Links, ``-Controls, Breite sowie ausgeklappte Setup-/Preview-Controls werden nicht geprüft. - Im Produkt ist der Attestierungs-Checkbox selbst mit `h-5 w-5` – also 20×20 px – definiert (`PortfolioPerformancePanel.vue:79`); nur das umgebende Label hat Mindesthöhe. - **Fix:** Jede relevante Ansicht öffnen und alle sichtbaren semantischen Controls einschließlich `a`, `summary` und Checkboxen auf eigene Bounding-Box `width >= 44 && height >= 44` prüfen oder den effektiven, nachweislich klickbaren Label-Hitbereich ausdrücklich messen. **Positiv verifiziert:** Requested-vs-available-Zeitraum, Null-/Unavailable-Anzeigen, Load-Generation gegen ältere Scope-Responses, Chart-Anker/gestrichelte Modelllinie sowie sichtbarer Overflow bei 1440/820/390. Fokussierte Tests: **23 Python + 13 Frontend bestanden**; `git diff --check` grün. **Dateien geändert:** keine – Review vollständig read-only.