merge: W7 coach memory, reminders, action cards into v1.22

This commit is contained in:
Marc-André Bombeck committed 2026-06-27 13:39:45 +02:00
commit e2c6e46f2a
33 files changed
+3913 -5

No files matched your search

+478
View File
@@ -4836,6 +4836,189 @@ paths:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
/api/coach/reminders:
get:
tags:
- Insights
summary: List the caller's Coach reminders
description: v1.22 (B2/B6) — the durable "remind me about X" memory the Coach captured inline, decrypted on the fly,
soonest-due first. Pass `?status=` (one status or a comma set like `due,surfaced` for the in-app tile); omitted
returns the non-terminal set (proposed + active + due + surfaced). Coach-gated
(`requireModuleEnabled("coach")`). Auth via cookie or Bearer; the owner is narrowed from the session.
Undecryptable rows are omitted.
parameters:
- name: status
in: query
required: false
schema:
type: string
description: Filter to one status or a comma-separated set (proposed | active | due | surfaced | done | dismissed). Omit
for the non-terminal set.
responses:
"200":
description: The caller's reminders.
content:
application/json:
schema:
$ref: "#/components/schemas/CoachRemindersList"
"401": *a1
"403":
description: Coach surface disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
post:
tags:
- Insights
summary: Create a Coach reminder manually
description: v1.22 (B6) — create a reminder from the ledger. `note` is the only writable content; `when` is the closed
grammar (ISO date | +Nd / +Nw | a context cue) resolved into the trigger server-side; `metric` optional. The
per-user cap returns 409 when the non-terminal set is full. Coach-gated. Auth via cookie or Bearer; the owner is
narrowed from the session.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderCreate"
responses:
"201":
description: The created reminder.
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderCreated"
"401": *a1
"403":
description: Coach surface disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"409":
description: The per-user reminder cap is full.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
/api/coach/reminders/{id}:
patch:
tags:
- Insights
summary: Confirm or update a Coach reminder's lifecycle
description: v1.22 (B6) — confirm a proposed reminder (→ active), mark it done / dismissed, or re-schedule via the
closed `when` grammar (null clears the due moment). The body never carries the note text. A foreign / unknown /
already-deleted id maps to 404 (never 403) so the existence channel does not leak. Coach-gated. Auth via cookie
or Bearer.
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Reminder id.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderPatch"
responses:
"200":
description: The reminder after the lifecycle update.
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderUpdated"
"401": *a1
"403":
description: Coach surface disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"404":
description: Reminder not found or not owned by the caller.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
delete:
tags:
- Insights
summary: Soft-delete one Coach reminder
description: "v1.22 (B6) — soft-deletes a single reminder owned by the caller. An unknown / cross-user / already-deleted
id is an idempotent no-op returning `{ deleted: false }`. Coach-gated. Auth via cookie or Bearer."
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Reminder id.
responses:
"200":
description: "The reminder was soft-deleted (`deleted: true`) or the id matched nothing the caller owns (`deleted:
false`)."
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderDeleted"
"401": *a1
"403":
description: Coach surface disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
/api/coach/suggested-actions:
post:
tags:
- Insights
summary: Confirm a Coach action card
description: v1.22 (F6) — the confirm half of the generalised propose→confirm moat. `actionType` is from the CLOSED
allowlist (checkup.create | reminder.note) — NEVER a medication or clinical change. `checkup.create` builds a
preventive-care Vorsorge `MeasurementReminder` (free-text label + a closed interval id resolved to an RRULE
server-side); `reminder.note` builds a `CoachReminder`. Nothing is created without this explicit confirm.
Coach-gated (`requireModuleEnabled("coach")`); per-user rate-limited (429). Auth via cookie or Bearer; the owner
is narrowed from the session, built field-by-field — no mass assignment, no IDOR.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CoachSuggestedAction"
responses:
"201":
description: The action was applied (entity created server-side).
content:
application/json:
schema:
$ref: "#/components/schemas/CoachSuggestedActionResultCreated"
"401": *a1
"403":
description: Coach surface disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"409":
description: The per-user reminder cap is full (reminder.note).
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
/api/admin/invites:
get:
tags:
@@ -8538,6 +8721,99 @@ components:
compatibility and are normalised to their English equivalents before persisting; GET responses always carry the
canonical English ids. Section ids are English-only. The legacy tile ids are deprecated and will be removed in a
future major version."
CoachReminderCreate:
type: object
properties:
note:
type: string
minLength: 1
maxLength: 280
when:
type: string
minLength: 1
maxLength: 32
metric:
type: string
minLength: 1
maxLength: 60
pattern: ^[A-Za-z][A-Za-z0-9_]*$
required:
- note
additionalProperties: false
description: "Create a Coach reminder manually. `note` is free text (the only writable content). `when` is an optional
closed grammar token — an ISO date (YYYY-MM-DD), a relative offset (+Nd / +Nw), or a context cue
(NEXT_BP_LOGGED, NEXT_WEIGHT_LOGGED, NEXT_SLEEP_LOGGED, NEXT_APP_OPEN) — resolved into the trigger server-side.
Omit `when` for a recall-only note. Strict: unknown keys 422."
CoachReminderPatch:
type: object
properties:
status:
type: string
enum:
- proposed
- active
- due
- surfaced
- done
- dismissed
when:
anyOf:
- type: string
minLength: 1
maxLength: 32
- type: "null"
additionalProperties: false
description: "Update a Coach reminder's lifecycle. `status` confirms (proposed → active), or marks done / dismissed.
`when` re-schedules via the closed grammar (null clears the due moment). Never carries the note text. Strict:
unknown keys 422."
CoachSuggestedAction:
oneOf:
- type: object
properties:
actionType:
type: string
const: checkup.create
label:
type: string
minLength: 1
maxLength: 120
interval:
type: string
enum:
- yearly
- halfYearly
- quarterly
- monthly
required:
- actionType
- label
- interval
- type: object
properties:
actionType:
type: string
const: reminder.note
note:
type: string
minLength: 1
maxLength: 280
when:
type: string
minLength: 1
maxLength: 32
metric:
type: string
minLength: 1
maxLength: 60
pattern: ^[A-Za-z][A-Za-z0-9_]*$
required:
- actionType
- note
description: Confirm a Coach action card. `actionType` is from the closed allowlist (checkup.create, reminder.note);
NEVER a medication or clinical change. `checkup.create` builds a preventive-care Vorsorge reminder (free-text
label + a closed interval id resolved to an RRULE server-side); `reminder.note` builds a CoachReminder (note +
optional closed `when` grammar + optional metric). Nothing is created without this explicit confirm.
type: object
CoachMessageFeedbackRequest:
type: object
properties:
@@ -17071,6 +17347,208 @@ components:
- data
- error
additionalProperties: false
CoachRemindersList:
type: object
properties:
data:
type: object
properties:
reminders:
type: array
items:
$ref: "#/components/schemas/CoachReminder"
description: The caller's reminders, soonest-due first. Undecryptable rows are omitted. No `status` filter returns the
non-terminal set (proposed + active + due + surfaced).
required:
- reminders
additionalProperties: false
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachReminder:
type: object
properties:
id:
type: string
note:
anyOf:
- type: string
- type: "null"
description: Decrypted note (the user's own framing). Null when the row's key id is no longer in the map.
metric:
anyOf:
- type: string
- type: "null"
triggerKind:
type: string
enum:
- date
- context
dueAt:
anyOf:
- type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
- type: "null"
contextCue:
anyOf:
- type: string
- type: "null"
status:
type: string
enum:
- proposed
- active
- due
- surfaced
- done
- dismissed
source:
type: string
enum:
- sentinel
- extractor
- manual
- action
createdAt:
type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
updatedAt:
type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
required:
- id
- note
- metric
- triggerKind
- dueAt
- contextCue
- status
- source
- createdAt
- updatedAt
additionalProperties: false
description: One Coach episodic reminder, decrypted server-side. The note reads null only when the encryption key id has
rotated out of the map.
CoachReminderCreated:
type: object
properties:
data:
type: object
properties:
reminder:
$ref: "#/components/schemas/CoachReminder"
required:
- reminder
additionalProperties: false
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachReminderUpdated:
type: object
properties:
data:
type: object
properties:
reminder:
$ref: "#/components/schemas/CoachReminder"
required:
- reminder
additionalProperties: false
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachReminderDeleted:
type: object
properties:
data:
type: object
properties:
deleted:
type: boolean
description: True when a reminder owned by the caller was soft-deleted; false for an unknown / cross-user /
already-deleted id (idempotent no-op).
required:
- deleted
additionalProperties: false
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachSuggestedActionResultCreated:
type: object
properties:
data:
$ref: "#/components/schemas/CoachSuggestedActionResult"
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachSuggestedActionResult:
type: object
properties:
ok:
type: boolean
const: true
actionType:
type: string
enum:
- checkup.create
- reminder.note
reminder: {}
reminderId:
type: string
required:
- ok
- actionType
additionalProperties: false
description: Outcome of confirming a Coach action card. `checkup.create` returns the created MeasurementReminder DTO;
`reminder.note` returns the created CoachReminder id.
AdminInviteList:
type: object
properties: