## Audit-Ergebnis Sprint 6I-C **Basis geprüft:** `a2053718d5eb077e1b44b7923e391f1ef9a1bfe3`, Branch `main`, Arbeitsbaum sauber. Es wurden **keine Repository-Dateien und keine Produktivdaten verändert**. Ein temporärer, synthetischer Read-only-Probe-Lauf wurde anschließend vollständig gelöscht. ### Wichtigste Befunde 1. **Today-Zähler zählt Dokumente mehrfach** - `data_provider.py:413–430` erzeugt mehrere überlappende Aufgaben pro Dokument. - Synthetischer Nachweis: - 2 ungeprüfte Dokumente - Gruppe `content_pending`: 2 - Gruppe `candidates`: 2 - `/api/v1/document-review-queue.total`: **4** - Zusätzlich begrenzt `tasks[:3]` in `data_provider.py:436` die Anzeige positionsabhängig; weitere relevante Aufgaben können verschwinden. - `document-processing` zählt aktuell alle ungeprüften Dokumente und bezeichnet sie pauschal als OCR/Hash/FTS-Systemarbeit, selbst wenn die technische Verarbeitung bereits abgeschlossen ist. 2. **Master-Detail ist bereits weitgehend vorhanden** - Desktop, mobile Liste/Detail, Back-Button, Fokus, sticky Aktionen und getrennte Statuszeile existieren bereits. - Für 6I-C ist kein zweiter Router und kein neues Backend-Modell nötig; eine gezielte Konsolidierung reicht. 3. **Verifizierte Suche ist backendseitig korrekt, frontendseitig nicht vollständig integriert** - `/api/v1/documents?q=…` erzwingt in SQL `d.review_status='geprueft'`. - `/matches` prüft den aktuellen Review-Status erneut. - Treffer-Snippets aus der API werden in der Dokumentliste jedoch nicht dargestellt. - `openMatches()` in `dashboard-v5-record.js:483–513` ist praktisch nicht in den aktuellen Master-Detail-Flow eingebunden. - Die lokale Suche `.document-local-search` durchsucht dagegen nur bereits geladene `reviewData.pages` clientseitig; sie ist **nicht** die verifizierte FTS-Suche. - Die globale Suche zeigt weder Snippet noch Trefferposition und öffnet ein Dokument ohne Navigation zum Trefferabschnitt. --- ## Minimaler Umsetzungsvorschlag ### 1. Today-Zählung korrigieren **Datei** - `scripts/health/dashboard_v5/data_provider.py:400–436` **Vorschlag** - Genau **eine** Today-Aufgabe für Dokumentprüfung: ```json { "id": "document-review", "label": "Dokumente prüfen", "count": 2, "action": "documents" } ``` - `count` als `COUNT(DISTINCT d.id)` über alle tatsächlichen Benutzerentscheidungen berechnen. - Immer `d.review_status='nicht_geprueft'` einbeziehen. - Die fachlichen Gruppen nur im Review-Center anzeigen, nicht als mehrfach gezählte Today-Aufgaben. - `tasks[:3]` nicht als versteckte Priorisierungslogik verwenden; entweder explizite Priorität oder ein stabiles aggregiertes Dokument-Task. - `data_preparation.document-processing` ausschließlich aus echten technischen Zuständen ableiten, nicht aus `review_status`. **Today-Selektoren** - `#today-tasks` - `.today-task-button` - `[data-task-action="documents"]` - `#today-data-preparation` - `#today-task-status` --- ### 2. Review-Queue-Vertrag eindeutig machen **Datei** - `scripts/health/dashboard_v5/read_api.py:3032–3057` **Bestehender Vertrag** ```json { "groups": [{ "code": "content_pending", "label": "...", "count": 2, "truncated": false, "first_document": "api-document-…" }], "total": 4 } ``` **Minimal kompatible Erweiterung** ```json { "groups": [...], "total_memberships": 4, "total_unique": 2 } ``` - Falls `total` aus Kompatibilitätsgründen bleiben muss, vorerst als Alias für `total_unique` dokumentieren oder explizit versionieren. - Jede Gruppenabfrage zusätzlich mit: ```sql d.review_status='nicht_geprueft' ``` - `first_document` bleibt eine opaque ID. - Gruppenzähler dürfen sich überlappen; nur `total_unique` darf als Today-Zähler verwendet werden. - Keine Schemaänderung notwendig. --- ### 3. Minimaler Master-Detail-Review-Center **Dateien** - `scripts/health/dashboard_v5/render.py:120–131` - `scripts/health/assets/health-assets/dashboard-v5-record.js:395–480` - `scripts/health/assets/health-assets/dashboard-v5.css:451–486, 516–529` **Bestehende Struktur weiterverwenden** - Shell: `#view-doctor.record-view` - Tab: `[data-record-tab="documents"]` - Inhalt: `[data-record-content]` - Workspace: `.document-workspace` - Master: `.document-master`, `.document-master-list` - Zeile: `.document-master-item` - Selektion: `.document-master-item[data-selected="true"]` - Detail: `[data-document-detail]` - Mobile Back: `.document-detail-back` - Aktionen: `.document-sticky-actions` - Herkunft: `.document-review-statuses` **Minimaler Zielzustand** - Desktop: Liste links, genau ein Detail rechts. - Mobile: Liste zuerst; Auswahl setzt `data-mobile-open="true"`, Back löscht `document` aus der Route und fokussiert den auslösenden Listeneintrag. - Master-Zeile nur: - Datum - Kategorie - Institution - Format - eine kombinierte Statuszeile - Treffer-Snippet nur bei aktiver FTS-Suche - Technische Engine-/Versionsangaben bleiben unter „Statusdetails und Datenherkunft“. - Bestehende Review-Formulare und `/health-actions/document-review` unverändert lassen. **Router-Vertrag beibehalten** ```text ?view=record&tab=documents ?view=record&tab=documents&document=api-document-<24 hex> ?view=record&tab=documents&document=…&preview=extracted ``` Zulässige URL-Parameter bleiben: - `view` - `tab` - `document` - `preview` Suchtext sollte wie bisher nicht in die URL gelangen; Filterzustand kann im History-State bleiben. --- ### 4. Verifizierte Suche tatsächlich nutzbar machen **Dateien** - `scripts/health/assets/health-assets/dashboard-v5-record.js` - `scripts/health/assets/health-assets/dashboard-v5-global-search.js` - `scripts/health/dashboard_v5/read_api.py` - `scripts/health/assets/health-assets/dashboard-v5.css` **API-Verträge beibehalten** - `GET /api/v1/documents?q=&limit=25` - nur aktuell geprüfte Dokumente - liefert `snippet`, `search_status`, opaque `id` - `GET /api/v1/documents/{id}/matches?q=` - nur aktuell geprüft - liefert: ```json { "id": "api-document-…", "query": "…", "matches": [{ "section": 3, "start": 12, "end": 18, "snippet": "…" }], "truncated": false } ``` - `GET /api/v1/search?q=…&include_machine=0|1` - Standard `0` - maschinelle Texte nur nach explizitem Opt-in **Frontend** - `item.snippet` in `.document-master-item` darstellen. - Treffer ausschließlich über Textknoten plus `` markieren; kein `innerHTML`. - Beim Öffnen eines FTS-Treffers: 1. Dokumentdetail öffnen, 2. `/matches` laden, 3. passenden Abschnitt nachladen, 4. `[data-section-number=""]` fokussieren/scrollen. - `openMatches()` in den Detailbereich integrieren, statt `[data-record-content]` durch eine isolierte Trefferkarte zu ersetzen. - Globale Dokumenttreffer sollten Snippet und Trust-Label anzeigen. - `window.healthRecordOpenDocument` minimal erweitern: ```js window.healthRecordOpenDocument(id, { query, targetSection }) ``` Suchtext bleibt nur im Speicher, nicht in URL oder Storage. **Such-Selektoren** - Dokumentfilter: `input[aria-label="Volltextsuche"]` - Detail-Suche: `[aria-label="Volltextsuche innerhalb dieses Dokuments"]` - Ergebnis-Markierung: `.document-search-snippet mark` - Trefferabschnitt: `[data-section-number]` - Global: - `[data-global-search-open]` - `[data-global-search-input]` - `[data-global-search-machine]` - `[data-global-search-results]` - `[data-global-search-status]` --- ### 5. 390px UX Bestehende Regeln unter `@media (max-width: 560px)` sind eine gute Basis. Ergänzend absichern: - Master-Buttons ebenfalls mindestens 44 px, nicht nur Detailkontrollen. - Detail-Back entfernt den selektierten Route-State, statt nur per CSS zurückzuschalten. - Fokus nach Auswahl auf Detailüberschrift; nach Back auf exakt den zuvor gewählten Master-Eintrag. - Sticky-Aktionsleiste darf den letzten Inhalt nicht verdecken: Detail braucht ausreichendes `padding-bottom`. - Lange Status-, Snippet- und Institutswerte mit `overflow-wrap:anywhere`. - Kein horizontaler Seitenoverflow bei 390×844. - `:has()` funktioniert im vorgesehenen Playwright/Chromium, bleibt aber ein Browser-Kompatibilitätsrisiko; eine explizite Workspace-Klasse wäre robuster. --- ## Maximal sechs Playwright-Fälle Neue fokussierte Datei: - `tests/browser/dashboard_v5_sprint6i_c.spec.js` Passende synthetische Fixture: - `tests/fixtures/dashboard_v5_sprint6i_c_fixture.py` Empfohlene Fälle: 1. **Today zählt eindeutige Dokumente** - genau ein Dokument-Task mit eindeutigem Count - keine doppelte Zählung durch überlappende Queue-Gruppen - technisch bereits verarbeitete Dokumente nicht fälschlich unter Datenaufbereitung 2. **Desktop Master-Detail** - `.document-master-item` → `[data-document-detail]` - nur eine Selektion/ein Detail - kombinierte Statuszeile, Herkunft eingeklappt - Original-/Preview-Aktionen gemäß Vertrag 3. **Verifizierte FTS-Suche** - Standard-Suche findet nur geprüftes synthetisches Dokument - Snippet mit sicherem `` - Klick navigiert zu passendem `[data-section-number]` - Reload behält Dokumentroute 4. **Maschinelle Suche bleibt explizit** - ungeprüfter Treffer fehlt bei `include_machine=0` - erscheint erst nach `[data-global-search-machine]` - Detail zeigt Warnung „noch nicht … geprüft“ - kein geprüfter Suchstatus wird suggeriert 5. **History und Fokus** - Today-Task → Review-Center - Auswahl → Back → Forward - URL, sichtbare Liste/Detail und Fokus bleiben konsistent 6. **390px Sicherheits-/UX-Gate** - kein horizontaler Overflow - alle sichtbaren Controls ≥44 px - List→Detail→Back funktioniert - keine externen Requests, keine Console-Errors, keine Pfad-/Token-/Roh-ID-Leaks Die Fixture sollte mindestens ein geprüftes, indexiertes und ein ungeprüft maschinell indexiertes synthetisches Dokument enthalten. Die aktuelle `dashboard_v5_sprint6i_fixture.py` enthält nur ungeprüfte Imports und kann daher den verifizierten Standard-Suchpfad nicht beweisen. --- ## Regressionsrisiken - **Hoch:** `total` oder Today-Zähler weiterhin als Summe überlappender Gruppen. - **Hoch:** Queue-Gruppen ohne aktuellen `review_status`-Join können bereits geprüfte Dokumente erneut anbieten. - **Hoch:** Maschinelle und geprüfte Treffer dürfen nicht still zusammengeführt oder gleich beschriftet werden. - **Mittel:** Integration von `openMatches()` kann bestehende Record-History oder stale-request-Isolation brechen. - **Mittel:** Bestehende ältere Browsertests erwarten teils „Dokumentenhistorie“ statt „Dokumentenprüfung“ und müssen auf den aktuellen sichtbaren Vertrag aktualisiert werden. - **Mittel:** `api_document_id()` hängt an Kategorie und Institution; Metadatenänderungen können alte opaque Deep Links ungültig machen. - **Mittel:** Technische Client-Filter wirken nur auf die geladene 25er-Seite; die bestehende Ehrlichkeitsnotiz muss erhalten bleiben. - **Niedrig/Mittel:** CSS-`:has()` bindet das mobile Umschalten an moderne Browserunterstützung. - **Niedrig:** „Weitere Dokumente laden“ ersetzt derzeit die Seite statt anzuhängen; Tests dürfen dies nicht fälschlich als kumulatives Laden beschreiben. ## Änderungen und Probleme - **Geänderte/erstellte Dateien:** keine. - **Produktivdaten:** nicht geöffnet oder verändert. - **Probleme:** keine Blocker; der synthetische Probe-Lauf bestätigte die doppelte Today-/Queue-Zählung reproduzierbar.