## Ergebnis des Read-only-Audits Geprüft wurde der Stand `d94a57e8f339dddad844b9cc4c5690f394495948` auf `sprint20/performance-data-readiness-v1`. Repository war und blieb sauber; `git diff --check` meldete keine Probleme. Keine Dateien geändert, keine produktive DB geöffnet und keine Provider-/Externzugriffe ausgeführt. ### Wichtigste Befunde 1. **Fehlende Teilwerte werden teilweise fälschlich als CHF 0 angezeigt** - `WealthCockpitPanel.vue:63` ruft für die Verteilung `money(item.value_chf)` auf. - `money()` verwendet `Number(null)`, was `0` ergibt (`:138`). - Damit wird ein unbekannter Verteilungswert als `CHF 0.00` dargestellt, obwohl Quellen und KPI sonst korrekt „nicht berechenbar“ behandeln. - Kleinste Korrektur: dort `moneyOrUnavailable()` verwenden und einen gezielten Null-vs.-Null-Test ergänzen. 2. **Die fünf fachlichen Dimensionen sind noch nicht sauber getrennt** - Aktueller Wert, Zeitraumänderung, Performance, Einzahlungen und Datenstand liegen gemeinsam in sechs KPI-Karten (`:22–31`). - Quellenstatus kombiniert Aktualität und Abgleich in einem String (`:151`), während `source.performance_status` überhaupt nicht angezeigt wird. - Policy ist als eigener Abschnitt getrennt, aber ihr fehlender Status erscheint zusätzlich als Top-Hinweis und nochmals im Abschnitt. - Empfohlen: kompakte, explizit beschriftete Statuszeile für: - aktueller erfasster Wert, - Aktualität, - Kontenabgleich, - Performance, - Portfolioorientierung. - Performancewerte und Policybewertung bleiben weiterhin in ihren eigenen Abschnitten. 3. **Der Datenstand-KPI trägt einen falschen Statussatz** - Für `data_as_of` liefert `kpiStatus()` wegen `status: available` den Text „Für den gewählten Zeitraum berechenbar“ (`:144`). - Das beschreibt einen Stichtag nicht. - Zudem ist `current.data_as_of` der **neueste** Stichtag irgendeiner Quelle (`wealth_cockpit.py:460–468`), nicht der einheitliche Stand des Gesamtvermögens. - Ehrlicher Text: „Neuester verfügbarer Quellenstichtag; einzelne Quellen können älter sein.“ 4. **Top-Hinweise sind serverseitig auf drei begrenzt, aber nicht robust dedupliziert** - Backend: `hints[:3]` in `wealth_cockpit.py:576–582,621`. - Frontend rendert den Payload ungefiltert (`WealthCockpitPanel.vue:29–31`). - Die drei Hinweise wiederholen inhaltlich Performance-, Diagnose- und Policyabschnitte. - Kleinste robuste Lösung: Frontend-Computed mit `trim → Set → slice(0,3)` und nur handlungsleitende Kurztexte. Detailbegründungen gehören an die jeweilige Quelle bzw. in den Abschnitt. 5. **Quellenspezifische Diagnose fehlt** - Jede Quellenkarte zeigt Wert, kombinierten Kurzstatus und Stichtag (`:94–103`). - Nicht sichtbar sind: - `performance_status`, - warum ein Abgleich nicht beurteilbar ist, - ob nur ein Gesamtwert statt Positionen vorliegt, - welche konkrete Quelle veraltet oder unvollständig ist. - Kleinste Änderung ohne API-Erweiterung: Diagnose direkt pro Karte aus den drei vorhandenen Statusfeldern ableiten und als verständliche Sätze anzeigen. - Keine Codes anzeigen; unbekannte Statuswerte fail-closed als „Nicht beurteilbar“ behandeln. 6. **API-Vertrag des Wealth-Cockpits ist schwächer als die übrigen Portfolio-Verträge** - `/portfolio/wealth-cockpit` hat kein FastAPI-`response_model` (`api/routers/overview.py:54–70`). - Im Frontend sind fast alle Statusfelder nur `string` (`api/portfolio.ts:39–53`). - Dadurch können Backend-/Frontend-Drift und unbekannte Zustände unbemerkt in irreführende Fallbacktexte laufen. - DB-Schema 49 muss dafür nicht geändert werden: - TypeScript-Statusunionen in `frontend/src/api/portfolio.ts`, - optional ein Pydantic-Response-Modell in `src/jarvis_finance/api/schemas/wealth_cockpit.py`, - dieses als `response_model` im Router eintragen. 7. **Technische Details sind nur teilweise eingeklappt** - Im Cockpit ist „Daten & Diagnose“ bereits ein geschlossenes `
` (`:113–122`). - Die Bezeichnung kollidiert jedoch mit dem gleichnamigen Haupttab. - Im Daten-Tab werden Fingerprint, Cutoff, Toleranzversion und interne Quellen im Standardmodus angezeigt (`DataIngestionReconciliationPanel.vue:43–57`). - Empfehlung: - Cockpit-Details in „Methodik & technische Details“ umbenennen. - Fachliche Lücken/Diagnosen außerhalb, technische Provenienz innerhalb. - Fingerprint, Cutoff, Contract-/Engine-Versionen und technische Quellen im Daten- und Performance-Tab ebenfalls in geschlossene `
` verschieben. - Reason Codes weiterhin ausschließlich auf deutsche Diagnosen abbilden. 8. **Veraltete Navigation behauptet weiterhin, Performance sei nicht gebaut** - Die echte Seite existiert unter `/portfolio/performance` (`router/index.ts:45`). - Gleichzeitig existiert `/analysis/performance` mit „Performancevergleich wird nicht in diesem Sprint gebaut“ (`router/index.ts:103`). - Das ist sachlich widersprüchlich. - Kleinste Lösung: alte Route auf `/portfolio/performance` umleiten oder entfernen; Navigationstest ergänzen. 9. **Provider-freies Rendern ist im geprüften Pfad grundsätzlich gegeben** - Frontend ruft standardmäßig nur same-origin `/api/portfolio/wealth-cockpit` auf. - Endpoint liest gespeicherte lokale Daten. - `get_equity_summary()`, `list_crypto_positions()` und `get_cash_summary()` lesen lokale Tabellen/Cache; kein Providerabruf im Cockpit-GET. - Risiko: `VITE_API_BASE_URL` kann einen externen Host konfigurieren. UAT sollte deshalb Netzwerkrequests explizit auf same-origin `/api/**` begrenzen und externe Requests als Fehler werten. ### Kleinster sinnvoller Änderungsumfang **Unbedingt:** - `frontend/src/components/wealth/WealthCockpitPanel.vue` - Nullwerte nicht als Nullbetrag formatieren. - maximal drei deduplizierte Top-Hinweise, - Wert/Aktualität/Abgleich/Performance/Policy getrennt darstellen, - pro Quelle separate Statuszeilen und verständliche Diagnose, - korrekter Text für Datenstand, - technische Methodik geschlossen halten. - `frontend/src/pages/PortfolioPage.test.ts` - bestehende Fixture und Assertions fokussiert erweitern. **Vertragshärtung:** - `frontend/src/api/portfolio.ts` - Statusunionen statt freier Strings; optional `diagnosis` nur dann, wenn serverseitig geliefert. - `src/jarvis_finance/api/routers/overview.py` - optional neu: `src/jarvis_finance/api/schemas/wealth_cockpit.py` - `tests/unit/test_wealth_cockpit_v1.py` **Navigation/technische Details:** - `frontend/src/router/index.ts` - `frontend/src/navigation/UserNavigationSmoke.test.ts` - für Standardmodus-Bereinigung zusätzlich: - `frontend/src/components/performance/PortfolioPerformancePanel.vue` - `frontend/src/components/portfolio-data/DataIngestionReconciliationPanel.vue` ### Fokussierte Tests 1. **Cockpit-Komponententest** - `null` in `distribution.value_chf` ergibt „Noch nicht verlässlich berechenbar“, niemals `CHF 0.00`. - vier oder mehr bzw. doppelte API-Hinweise ergeben höchstens drei eindeutige sichtbare Hinweise. - `data_as_of` zeigt keinen Berechenbarkeitsstatus. - jede Quellenkarte zeigt Wert, Aktualität, Abgleich und Performance getrennt. - `not_assessable`, `difference`, `stale`, `not_calculated` erzeugen verständliche quellennah platzierte Diagnosen. - Reason Codes, Fingerprints und Engine-Versionen fehlen im Standardmodus. - technische Details sind initial geschlossen. 2. **API-Vertragstest** - exakt erlaubte Statuswerte, - maximal drei eindeutige Hinweise, - fehlend bleibt `null`, nicht numerische Null, - GET bleibt read-only (`conn.total_changes` unverändert), - keine DB-Migration; Schema 49 unverändert. 3. **Navigationstest** - `/portfolio`, `/portfolio/performance`, `/portfolio/data`, `/portfolio/strategy` lösen auf konkrete Ansichten auf. - `/analysis/performance` redirectet auf die echte Performanceansicht und zeigt keine veraltete Roadmapmeldung. ### UAT-Risiken bei 1440 / 820 / 390 px - **390 px:** `body { overflow-x:hidden }` kann echte Überbreite nur verdecken. UAT muss `document.documentElement.scrollWidth <= clientWidth` prüfen, nicht nur visuell. - Lange Quellenlabels, Statusdiagnosen und CHF-Werte brauchen `min-w-0` plus `overflow-wrap:anywhere`. - Der Portfolio-Subnav muss umbrechen, ohne abgeschnittene oder unter 44 px hohe Ziele. - **820 px:** Zwei-Spalten-Quellenkarten und lange Diagnosen auf Überlappung prüfen. - **1440 px mit 288-px-Sidebar:** Tailwind-`xl` reagiert auf den Viewport, nicht auf die tatsächlich schmalere Content-Spalte. Drei Quellen-/KPI-Spalten und die 1.6/1-Historienaufteilung deshalb mit langen Texten prüfen. - Technische `
` geöffnet und geschlossen in allen drei Viewports testen. - Netzwerk-UAT: nur Dokument, statische lokale Assets und same-origin `/api/**`; kein CoinGecko, FMP, Yahoo, Bild-CDN oder sonstiger Provider. - Ein echter Browser-/Playwright-Viewporttest für das Wealth-Cockpit existiert derzeit nicht; vorhandene mobile Tests prüfen überwiegend Klassen und andere Seiten, keine reale Geometrie oder horizontale Überbreite. **Dateien erstellt oder geändert:** keine. **Probleme:** keine Tool- oder Repository-Blockade; aus Read-only-Gründen wurden keine Builds, Browser-Server oder Tests mit potenziellen Artefaktwrites gestartet.