fix(consent): mint AI consent receipts only on an explicit agreement

The AI settings page posted to /api/consent/ai/web on every mount and the
server minted an ai_full receipt for it, so an account that had agreed to
nothing sent its health data to the operator's provider from background
jobs. Native builds synced a receipt the same way on every launch.

- Both grant routes now require `confirm: true`; anything else writes
  nothing and answers 400 consent.ai.confirmRequired.
- Remove the mount-time heal. A consent card names the operator's service
  and the data that would go there, with one Agree button, in Settings > AI
  and on the Insights overview while AI is closed by the missing consent
  alone.
- Publish the standing consent as `ai.consent` on /api/auth/me (status,
  since, recipients); the settings card renders active-since and the
  withdraw control from it.
- Existing receipts stay valid.
- Guard that no client grants outside a click handler, route tests for the
  confirmation, and an integration test that a fresh operator-key account
  gets no background AI text until it agrees.
This commit is contained in:
Marc-André Bombeck committed 2026-10-16 17:11:04 +02:00
1 parent bc5ea5a67b
commit e2b7e7350e
45 files changed
+1707 -678

No files matched your search

+1 -1
View File
@@ -112,7 +112,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"coach-prefs.body.invalid_json",
"coach-prefs.body.invalid_shape",
],
consent: ["consent.ai.required"],
consent: ["consent.ai.confirmRequired", "consent.ai.required"],
cycle: [
"cycle.bulk.invalid",
"cycle.bulk.too_large",