fix(vorsorge,coach): align reminders + coach chrome with the gold standard

Twice-daily BP protocol (RRULE BYHOUR=7,19) now fires at every clock
hour: byHourTimesOfDay expands BYHOUR into the engine's timesOfDay so
the label, RRULE, and engine output agree instead of collapsing to one
nudge at notifyHour.

Vorsorge surface:
- move create/edit into a ResponsiveSheet with a pinned Cancel/Save
  footer, matching Labs/Illness/Medications
- wire the unused PATCH support: Edit affordance opens the form
  pre-filled; an enable/disable Switch drives the enabled flag
- translate COACH-origin labels (the row stores an i18n key, not prose)
  and render a neutral Coach provenance badge + an until-<date> line
- surface origin + endsOn on the hook type
- pull-to-refresh + always-on rationale documented on the nav entry
- NativeSelect-vs-Radix choice documented as deliberate

Coach:
- expired COACH course-window reminders self-clean: the cron tick
  soft-deletes finished finite cadences instead of leaving dead rows
- accept-dedup gains a partial unique index (user, metric) where
  origin=COACH and not deleted, with a P2002 catch in the route
- per-user rate-limit bucket on the suggestion-action endpoint
- SUGGEST-REMINDER parser keeps prose written after the block
- document POST /api/coach/reminder-suggestions in OpenAPI

Coach chrome:
- HistoryRail hideHeading prop drops the duplicate Conversations
  heading on the page surface
- align the rail-tray strip inset to px-3 sm:px-4 so the dividers line
  up; reword the FAB inset comment

satisfyReminder closes the forward-only TOCTOU via a conditional
updateMany (count===0 no-op). Migration headers corrected to v1.18.1.
This commit is contained in:
Marc-André Bombeck committed 2026-06-16 23:11:08 +02:00
1 parent b28fa4d4a5
commit 7451e886f1
32 files changed
+983 -230

No files matched your search

+114
View File
@@ -4387,6 +4387,59 @@ paths:
"401": *a1
"422": *a3
"429": *a4
/api/coach/reminder-suggestions:
post:
tags:
- Insights
summary: Act on a Coach cadence suggestion
description: 'v1.18.1 — accept / dismiss / stop a Coach-proposed measurement cadence. Coach-gated
(`requireModuleEnabled("coach")`); a disabled surface 403s. `accept` resolves the cadence from the closed
server-side catalog and creates a `MeasurementReminder` with `origin: COACH` (201), or returns 200 with
`duplicate: true` when a live COACH reminder for that metric already exists — idempotent against a re-tapped or
stale card, with a partial unique index as the structural backstop. Per-user rate-limited (429 on excess). Auth
via cookie or Bearer; the owner is narrowed from the session.'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
cadenceId:
type: string
minLength: 1
maxLength: 64
action:
type: string
enum:
- accept
- dismiss
- stop
required:
- cadenceId
- action
responses:
"200":
description: "A dismiss/stop, or an accept that matched an existing reminder (`duplicate: true`)."
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderSuggestionResultOk"
"201":
description: An accept that created a new reminder.
content:
application/json:
schema:
$ref: "#/components/schemas/CoachReminderSuggestionResultCreated"
"401": *a1
"403":
description: Coach surface (or the cadence's module) disabled.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
"422": *a3
"429": *a4
/api/admin/invites:
get:
tags:
@@ -15269,6 +15322,67 @@ components:
- data
- error
additionalProperties: false
CoachReminderSuggestionResultOk:
type: object
properties:
data:
$ref: "#/components/schemas/CoachReminderSuggestionResult"
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
CoachReminderSuggestionResult:
type: object
properties:
ok:
type: boolean
const: true
action:
type: string
enum:
- accept
- dismiss
- stop
reminder:
description: "On `accept` of a fresh suggestion: the created MeasurementReminder DTO. Null when the action was a
prefs-only dismiss/stop, or when an accept hit the structural dedup (`duplicate: true`)."
anyOf:
- {}
- type: "null"
duplicate:
description: True when an `accept` matched an already-live COACH reminder for the same metric (idempotent no-op).
type: boolean
required:
- ok
- action
additionalProperties: false
description: "Outcome of a Coach cadence-suggestion action. `accept` returns 201 with the created reminder (or 200 +
`duplicate: true` when one already exists); `dismiss`/`stop` return 200."
CoachReminderSuggestionResultCreated:
type: object
properties:
data:
$ref: "#/components/schemas/CoachReminderSuggestionResult"
error:
type: "null"
meta:
type: object
properties:
requestId:
type: string
additionalProperties: false
required:
- data
- error
additionalProperties: false
AdminInviteList:
type: object
properties: