# Sprint 5C — Dashboard v5 Mobile Erfassung über private Action Queue

**Status:** produktiv in der parallelen v5-Preview deployt (2026-07-13)

**Primärpfad:** `/health-dashboard-v5` → `Beschwerden erfassen`

**Fallback:** Dashboard v4 und seine Route blieben beim freigegebenen Deployment bytegleich unverändert.

## Ziel

Sprint 5C macht die bereits angelegte mobile Beschwerdeerfassung zu einem belastbaren, privaten Queue-Workflow. Der netzwerkseitige Dashboard-Prozess schreibt niemals direkt in die Gesundheitsdatenbank. Er validiert eine vollständige Tageserfassung, legt eine streng begrenzte private Action-Datei atomar ab und übergibt sie einem getrennten lokalen Worker.

## Lieferumfang

1. sieben verpflichtende Beschwerdedimensionen mit Werten `0` bis `3`,
2. optionale Notiz mit maximal 300 Zeichen,
3. bei jedem Abruf serverseitig gesetztes und sichtbar ausgewiesenes Tagesdatum in `Europe/Zurich`,
4. exakte Form-Shape- und Single-Value-Validierung,
5. exakte `http://<Host[:Port]>`-Origin-Prüfung und einmalig nutzbares CSRF-Token,
6. Rückkehr ausschließlich auf eine fest erlaubte Dashboardroute,
7. einmalig nutzbare, kurzlebige Queue-Bestätigung statt frei vortäuschbarem Erfolgsparameter,
8. private, atomare und auf 64 ausstehende Aktionen begrenzte Queue,
9. erneute vollständige Payloadvalidierung im lokalen Worker,
10. explizite Datenbankweitergabe an Quick Add, Korrelationen und v5-Regeneration,
11. mobile und tastaturbedienbare Dialogführung mit mindestens 44×44 CSS-Pixel großen Zielen.

## Sicherheitsvertrag

- Der Netzwerkserver schreibt keine Datenbankzeile.
- Queue-Verzeichnis: Eigentümer muss der laufende Benutzer sein, Modus wird auf `0700` repariert.
- Action-Dateien: reguläre Dateien, gleicher Eigentümer, exakt Modus `0600`, maximal 4096 Byte, kein Symlink-Following.
- JSON akzeptiert keine doppelten Schlüssel und nur die kanonische Payloadform.
- Unbekannte, fehlende oder mehrfach vorhandene Formularfelder werden abgelehnt.
- Zukunftsdaten, unvollständige Scores und Werte außerhalb `0..3` werden abgelehnt.
- Das Erfassungsdatum stammt nicht aus dem möglicherweise älteren statischen Bundle; Abruf, Worker, Quick Add und v5-Regeneration verwenden unveränderlich dieselbe Zürich-Tagesgrenze. Eine externe `HEALTH_DASHBOARD_TIMEZONE`-Variable kann den Vertrag nicht abweichend konfigurieren.
- Numerische Null bleibt eine echte Beobachtung; fehlende Werte werden nie als Null gespeichert.
- Queue-Bestätigungen sind einmalig, höchstens 120 Sekunden gültig und bestätigen nur die Vormerkung — nicht die erfolgreiche Datenbankverarbeitung.
- Der Worker übergibt die konfigurierte Datenbank explizit an Mutation, Korrelationen und v5-Generator und läuft über den getesteten Hermes-Python-Interpreter mit den erforderlichen Statistikabhängigkeiten.
- Worker-Start ohne `HEALTH_DASHBOARD_DB` und Quick Add ohne `--db` brechen vor Queue- beziehungsweise SQLite-Zugriff fail-closed ab; es gibt keinen impliziten Produktionsfallback.
- Quick Add validiert Datum, bekannte Dimensionen, Integerwerte `0..3` und die 300-Zeichen-Grenze nochmals unmittelbar vor jeder Mutation.
- Die Legacy-Dashboardpipeline wird für eine abweichende Datenbank fail-closed verweigert; synthetische Tests können dadurch nicht implizit auf Produktion wechseln.
- systemd setzt `UMask=0077` und `StateDirectoryMode=0700`.
- `Referrer-Policy: strict-origin` erhält einen prüfbaren Same-Origin-POST, ohne Receipt-Pfad oder Query an Asset-Requests weiterzugeben.

## Nicht-Ziele

- keine direkte Datenbankmutation aus dem HTTP-Prozess,
- keine Medikamentengabe-, Ernährungs- oder Laborerfassung ohne eigenen validierten Fachvertrag,
- keine partielle Tagesgesamtsumme,
- keine Diagnose-, Dringlichkeits-, Kausalitäts- oder Therapieaussage,
- keine Gesundheits-, Score-, Notiz- oder Formulardaten in URL, `localStorage` oder externen Diensten; die URL enthält höchstens das kurzlebige nicht-medizinische Einmal-Receipt,
- keine Änderung oder Ablösung der v4-Fallbackroute.

## Bestätigungssemantik

Nach erfolgreichem Queue-Schreiben lautet die Meldung sinngemäß:

> Sicher zur lokalen Verarbeitung vorgemerkt. Der Eintrag erscheint nach erfolgreicher Verarbeitung im Dashboard.

Die Meldung wird nur nach einem gültigen, serverseitig ausgestellten Einmal-Receipt gezeigt. Ein frei gesetzter oder wiederverwendeter `queued`-Wert erzeugt keine Bestätigung. Der Queryparameter wird anschließend aus der sichtbaren URL entfernt.

## Synthetische Abnahme

- vollständiger POST mit CSRF, Same-Origin und `return_to=v5` → `303` zurück zur v5-Route,
- unbekannte oder doppelte Formularfelder → Ablehnung,
- CSRF-Replay → Ablehnung,
- Queue-Verzeichnis `0700`, Dateien `0600`, keine temporären Restdateien,
- Queue-Limit wird fail-closed abgelehnt,
- Worker lehnt unsichere Dateimodi und doppelte JSON-Schlüssel ab,
- vollständiger synthetischer Queue→Worker→SQLite→v5-Durchlauf mit sieben Dimensionen und `integrity_check=ok`,
- echter nativer Browser-POST bei 390×844: vollständige Pflichtfelder, Origin-Prüfung, Queue-Datei `0600`, Einmal-Bestätigung und bereinigte URL,
- Browser-Testmodus nur mit übereinstimmender synthetischer Instanz-ID und einer Action Queue unter `/tmp`; dadurch wird ein POST gegen eine nicht ausgewiesene Runtime abgelehnt,
- Dialogprüfung: erster Fokus auf dem ersten Score, Fokusführung, 44px-Ziele und Reflow bei 200-%-Text,
- kostenoptimierter `gpt-5.3-codex-spark`-Worker für einen read-only Diff-Audit eingesetzt; sein DB-Isolationsfinding wurde testgetrieben behoben.

## Release-Gates

- vollständige Python-Suite: **150 bestanden**,
- vollständige Playwright-Suite: **17 bestanden**,
- Ruff, Python-Kompilierung, JavaScript-Syntax, systemd-Verifikation und `git diff --check`: **bestanden**,
- synthetische Browser- und Queue-Abnahme ohne Produktionsdaten: **bestanden**,
- Produktionsabnahme: Dienste aktiv, Datenbankintegrität intakt, Queue leer und auf `0700` migriert,
- UX/Accessibility-Review: **APPROVE**,
- Medical-Safety/Data-Contract-Review: **APPROVE**,
- Security/Privacy/Release-Review: **APPROVE**,
- freigegebene Commits `075befa` und `c3db3f8` auf `main`; lokale Runtime stimmt mit dem Repository überein,
- `/health-dashboard` und `/health-dashboard-v5` lieferten nach Deployment HTTP 200; v4 blieb unverändert.
