Schliesst hans-muellers K1-Befund (Datenschutz-Audit): der Coach-Prompt
sendete Identifier + Art.9-nahe Daten an US-LLMs (Gemini/OpenAI/Anthropic).
- message.post.ts: Geburtsjahr/exaktes Alter -> Altersgruppe (Dekaden-Bucket);
Stadt komplett entfernt (Bundesland bleibt). Geschlecht/Familienstand/Beruf/
Nickname unveraendert (gewollte Personalisierung; Nickname = Pseudonym).
- lyraMemoryExtract.ts: Extraction-Prompt reduziert Dritt-Klarnamen auf Rolle
("Frau Maria" -> "seine Frau"), keine Orte/Arbeitgeber im Memory-Content.
- 08-datenschutz-audit: Payload-Audit-Platzhalter durch Vorher/Nachher-Tabelle
ersetzt, K1 erledigt, ZDR-Update (DPA/SCCs deemed-signed, TIA offen).
Pseudonymisierung zaehlt jetzt als zweite Schutzmassnahme neben ZDR fuer die TIA.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The old streak was non-functional: streaks.current_days was always 0 (never
computed/incremented), and the profile page read me.streak (0) + account
created_at as the "since" date — showing "0 days protected since <signup>"
for everyone. This is the DiGA key metric, so it had to be rebuilt.
New model: optimistic protection-coverage based on actual VPN/MDM protection
state, never resets to 0.
- backend: append-only protection_state_log + migration; POST /api/protection/event
(ingestion, deduped) + GET /api/protection/coverage (read-time compute, no cron);
server-side cooldown_disable event on cooldown resolve. Generous >6h-off/day rule.
- frontend: report protection on/off transitions (initial + flips, deduped) from
useProtectionState; rewrote profile StreakSection → half-donut (protected vs
unprotected) + progress bar (current streak → personal record) + empty state.
- coverage starts fresh from deploy (no historical backfill — clean data for DiGA).
- spec: docs/specs/protection-coverage-streak.md (shared contract).
- old streaks/streak_events/profiles.streak left intact (coach/scores consumers).
Also adds go-to-market one-pagers under docs/marketing/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>