## Ergebnis der Read-only-Analyse **Stand:** `80bf712960f154c5ba6ec6e32f9eecb442426778`, Arbeitsbaum unverändert. **Verifikation:** `PYTHONPATH=. pytest -q ...` für Sprint 6B/6D/6E: **36 Tests bestanden**. Die wahrscheinlichen Fehler liegen daher vor allem in realer Datenmenge, Fehlerkopplung und fehlender UI-Integration, nicht in den synthetischen Happy-Path-Verträgen. ### Wahrscheinliche reale Fehlerursachen 1. **Akte-Übersicht zeigt interne Felder und kann an einem Eintrag komplett scheitern** - `dashboard-v5-record.js:350–360`, besonders **Zeile 355**: ```js Object.values(item) .filter(value => typeof value !== 'object') .join(' · ') ``` - Dadurch werden ungefiltert skalare Vertragsfelder angezeigt, etwa: - `api-document-…`, `lab-observation-…` - `metric_id` - `verified_original` - `verified_observation_specific` - `documented_visit` - `geprueft` - technische Quell- und Statuswerte - Feldreihenfolge und Anzeige hängen zudem von der JSON-Objektreihenfolge ab. Ein `null`/unerwarteter Nicht-Objekt-Eintrag führt bei `Object.values(item)` zum Abbruch der **gesamten Übersicht**. - Zweite `Object.values`-Abhängigkeit in `dashboard-v5.js:304` und `:413`; diese betrifft Bundle-Zeitreihen und ist weniger direkt für die Akte relevant. 2. **Breite Akte- und Kalenderrequests können erst mit realen Daten in das 1-Sekunden-Limit laufen** - `read_api.py:126–133`: pro DB-Verbindung wird ein fester Deadline-Zeitpunkt von nur **1,0 s** gesetzt. - `/api/v1/record-summary` (`read_api.py:2072–2103`) führt kumulativ Dokument-, Medikament-, Termin- und vollständige Laborabfragen aus. - `/api/v1/calendar` (`read_api.py:1301–1431`) iteriert über freigegebene Apple-Quellen und führt anschließend mehrere gruppierte Tabellenabfragen aus. - Das Zeitbudget wird nicht pro SQL-Abfrage erneuert. Auf einer real großen DB kann daher eine spätere, an sich korrekte Query mit `sqlite3.OperationalError` enden und als `503 data_unavailable` erscheinen. Kleine Fixtures decken das nicht ab. - Frontendseitig bleibt davon nur „Akte/Kalenderdaten sind derzeit nicht verfügbar“. 3. **Kalender ist unnötig an den vollständigen Metrikkatalog gekoppelt** - `dashboard-v5-day-controller.js:25–35` lädt vor jeder Kalenderinitialisierung: ```text GET /api/v1/metric-catalog?q= ``` - Erst nach Erfolg von `serverContractReady` werden Monatsrequests gestartet (`:276–281`). - Der Katalog ruft zusätzlich `inventory_sources()` auf (`read_api.py:2282–2307`). Ein Katalog-/Inventarfehler verhindert somit den Kalender, obwohl `GET /api/v1/calendar` selbst gesund sein kann. - Die `.then(...)`-Ketten besitzen keinen abschließenden `.catch(...)`. Bei Bootstrapfehlern kann der Kalender leer bleiben und nur eine unbehandelte Promise-Rejection in der Konsole erzeugen. 4. **Globale Suche ist backendseitig vorhanden, im Header aber vollständig unverdrahtet** - Backend: `GET /api/v1/search?q=…`, `read_api.py:933–973`, Dispatch `:2336–2338`. - `render.py:115–117` enthält im Header nur Datum, Datenstand, Privacy und Mehr; **kein Suchfeld, keine Ergebnisregion**. - `dashboard-v5.js` enthält keinen Aufruf von `/api/v1/search`. - Damit ist die globale Suche trotz getesteter API für Benutzer nicht erreichbar. 5. **Globale Suche kann bei vorhandener FTS für gültige allgemeine Suchbegriffe komplett ausfallen** - `_search()` akzeptiert grundsätzlich normalisierte Begriffe mit **2–80 Zeichen** (`read_api.py:933–936`). - Sie ruft aber zuerst `_fts_search_documents()` auf (`:964`), welche den strengeren Dokument-FTS-Vertrag übernimmt (`:911–930`, `_fts_tokens` `:1703–1710`). - Beispiele: - Datum `2026-06-13` - Alias `D-Dimer` - andere Suchbegriffe mit Bindestrich oder Satzzeichen - Diese können vom allgemeinen Suchvertrag akzeptiert, vom FTS-Tokenizer aber mit `query_not_allowed` verworfen werden. Der Fehler einer einzigen Gruppe verhindert dann auch Metrik-, Labor- und Tagesresultate. - Der vorhandene Datumstest läuft nur zuverlässig, solange keine FTS-Tabelle vorhanden ist; mit produktiver FTS wird dieser Pfad anders ausgeführt. 6. **Generische Fehlerbehandlung verhindert Diagnose und tab-isolierte Darstellung** - Akte: - Dokument: `dashboard-v5-record.js:170–172` - Volltexttreffer: `:241–243` - alle Tabs: `:382–384` - Tag: `dashboard-v5-day-controller.js:176–178` - Kalender: `:258–260` - `request()` wirft lediglich `api_${response.status}` und liest den JSON-Fehlerkörper nicht. - Der Server liefert zwar strukturierte Codes: ```json {"error":{"code":"data_unavailable"}} ``` in `health_dashboard_server.py:872–886`, doch im UI werden weder Fehlerklasse noch Retry-Möglichkeit noch tabbezogener Fehlerzustand unterschieden. - Bei Recordfehlern wird vorher `content.replaceChildren()` ausgeführt; der Benutzer sieht danach nur leeren Inhalt plus globale Statuszeile. Andere Tabs bleiben technisch klickbar, der Fehler ist aber nicht als lokaler Zustand des betroffenen Tabs dargestellt. ### Stellen mit sichtbaren internen Codes statt deutscher Labels - `dashboard-v5-record.js:261`: - `reference_status`, `quality` - `dashboard-v5-record.js:284`: - Terminstatus `documented_visit` - `dashboard-v5-record.js:355`: - sämtliche skalaren Felder über `Object.values` - `dashboard-v5-record.js:316`, `:318–319`: - `canonical_read_only_health_database`, `documented`, `unknown`, `complete_page` - `dashboard-v5-day-controller.js:125`: - technische Aggregations- und Qualitätswerte - `dashboard-v5-day-controller.js:133–135`: - `planned`, `administered`, `missed`, `corrected` - `dashboard-v5-day-controller.js:147`: - Terminstatus - `dashboard-v5-day-controller.js:224`: - `has_measurements` wird nur zu `measurements`, usw.; weiterhin englischer interner Code - `dashboard-v5-day-controller.js:182–187`: - unbekannte Kategorie fällt direkt auf den internen Wert zurück - `dashboard-v5-day-controller.js:152`: - rohe technische JSON-Anzeige ist zwar in einem Details-Bereich, sollte aber als bewusst technische Ansicht gelten. ## Konkrete Queryverträge ### Akte - `GET /api/v1/record-summary` - keine Queryparameter - `GET /api/v1/record-labs` - `q`, `from`, `to` - `GET /api/v1/medications` - `from`, `to` - `GET /api/v1/appointments` - `from`, `to`, `order=asc|desc`, `institution` - `GET /api/v1/documents` - `from`, `to`, `category`, `institution`, `type`, `review_status` - `sort=document_date_desc|document_date_asc|import_date_desc|category|institution|type|review_status` - `q`, `cursor`, `limit=1..50` - `GET /api/v1/documents/{api-document-ID}` - `cursor`, `limit=1..50` - `GET /api/v1/documents/{api-document-ID}/matches` - `q` erforderlich, FTS-Literalvertrag - `GET /api/v1/documents/{api-document-ID}/original` - keine Queryparameter - `GET /api/v1/doctor-report` - `from` und `to` erforderlich - `sections` als kommaseparierte Teilmenge von: `overview,labs,medications,symptoms,appointments,documents` ### Kalender/Tag - `GET /api/v1/calendar` - `from`, `to` erforderlich - inklusive Spanne maximal 62 Tage - Ende maximal `today + 366 Tage` - `GET /api/v1/day/YYYY-MM-DD` - keine Queryparameter - Datum maximal `today + 366 Tage` - Derzeitiger unnötiger Bootstrap: - `GET /api/v1/metric-catalog?q=` ### Globale Suche - `GET /api/v1/search?q=…` - exakt ein `q` - normalisiert 2–80 Zeichen - Ergebnisgruppen: `metrics`, `laboratory`, `symptoms_events`, `documents`, `days` - Dokumentgruppe besitzt faktisch den zusätzlichen strengeren FTS-Tokenvertrag. ## Minimaler Reparaturplan 1. **Akte zuerst stabilisieren** - `Object.values` durch explizite, tab-/Entitätsspezifische Presenter ersetzen. - Feste deutsche Feldreihenfolge und zentrale Labelmaps für Status, Qualität, Review und Provenienz. - Pro Recordtab eigenes Fehlerpanel mit „Erneut laden“; Tabs und bereits geladene Inhalte nicht durch einen fremden Tabfehler zerstören. - Fehlerkörper lesen, intern protokollierbaren Code behalten, Benutzertext aber aus einer deutschen Allowlist beziehen. 2. **Kalender entkoppeln** - Kalender nicht über `/metric-catalog?q=` bootstrappen. - `today`/`timezone` entweder aus dem validierten Bundle oder direkt aus der Kalenderantwort übernehmen. - Promise-Rejections zentral abfangen. - Monatscodes über dieselbe deutsche Kategorie-Map wie Agenda und Tagesansicht rendern. 3. **Globale Suche minimal integrieren** - Suchformular im Header, Ergebnisregion/Dialog mit Status und leerem Zustand. - Debounce oder explizites Submit; Mindestlänge 2. - Treffer nach den fünf API-Gruppen mit festen deutschen Labels rendern. - Drill-down: - Tag → `healthDayRouter.open` - Dokument → Akte/Dokument - Metrik/Labor → Explorer bzw. Akte-Labor. 4. **Suchgruppen backendseitig isolieren** - Dokument-FTS darf einen sonst gültigen Suchrequest nicht komplett abbrechen. - FTS nur für kompatible Tokens ausführen oder einen kontrollierten leeren Dokumentgruppenstatus liefern. - Regressionstests mit **aktiver FTS** für Datum und `D-Dimer` ergänzen. 5. **Reale Last absichern** - Record-summary und vollständigen Kalendermonat gegen eine DB mit produktionsnaher Zeilenzahl messen. - Danach entweder Queries/indexseitig begrenzen/optimieren oder das starre kumulative 1-Sekunden-Budget gezielt anpassen; nicht pauschal den Fehler im Frontend verstecken. **Änderungen/Dateien:** keine. **Einziger Testhinweis:** Ohne `PYTHONPATH=.` scheiterte die gemeinsame pytest-Sammlung an `tests.fixtures`; mit dem korrekten Repo-Importpfad liefen alle 36 fokussierten Tests grün.