fix(notifications): apns self-test fires MEDICATION_REMINDER for lock-screen parity

The per-channel "Send test" button on the Settings notification card hit
`/api/notifications/apns/test`, which forwarded `eventType: "SYSTEM_ALERT"`
to the dispatcher. The dispatcher's `isTimeSensitive` gate keys off
MEDICATION_REMINDER specifically, so the test push went out at the default
`active` interruption-level. iOS then summarises those into the Notification
Center rather than presenting a lock-screen banner, which leaves the user
with no signal whether a real dose reminder — which DOES use the
time-sensitive path — will surface visibly.

Switching the test event-type to MEDICATION_REMINDER exercises the same
dispatcher branch the production reminder cron runs: time-sensitive
interruption-level, priority 10, the MEDICATION_REMINDER action category,
collapse-id. A successful self-test now demonstrates the exact lock-screen
behaviour the user can expect from a real dose reminder; an unsuccessful
self-test (sound-only, Notification-Center-only) points the user at their
own iOS Settings → HealthLog → "Allow Time Sensitive Notifications" toggle
or active Focus mode rather than appearing to be a server-side bug.

Title and body strings remain the existing "Test notification" copy so the
push cannot be confused with an actual scheduled dose.
This commit is contained in:
Marc-André Bombeck committed 2026-05-23 12:45:11 +02:00
1 parent 05e826eb2f
commit 4b65298afe
2 files changed
+10 -1

No files matched your search

+1
View File
@@ -35,6 +35,7 @@ v1.4.47 closed the H1 + Mediums punch list. v1.4.49 bundles the deferred v1.4.47
- Workout-batch integration tests (`workout-batch-create`, `workout-batch-race`) updated to match the v1.4.42 cross-source dedup semantics. The integration suite had been red on `main` since v1.4.45 (three release commits shipped through it). Suite is green again.
- Twelve stale "migration 0076" references in code comments corrected. `consecutiveFailures` sites read 0077; `disable-coach` sites read 0078. Comment-only sweep, no behavioural change.
- `docs/ops/attempt-number-semantics.md` referenced migration 0076 for the dropped `consecutiveFailures` column; the actual migration is 0077.
- Per-channel APNs "Send test" button (Settings → notification channels) now fires with `eventType: "MEDICATION_REMINDER"` so the dispatcher's `time-sensitive` interruption-level + `apns-priority: 10` branch runs. The previous `SYSTEM_ALERT` event-type used the dispatcher's default `active` level, which iOS may summarise into the Notification Center rather than presenting a lock-screen banner — leaving the user with no signal whether a real dose reminder will surface visibly. A successful test now exercises the exact path production reminders take. Title and body strings still read "Test notification" so the push cannot be mistaken for an actual scheduled dose.
### Performance
+9 -1
View File
@@ -57,10 +57,18 @@ export const POST = apiHandler(async (request: NextRequest) => {
: defaultLocale;
const t = getServerTranslator(locale).t;
// Send as MEDICATION_REMINDER so the dispatcher's time-sensitive /
// priority-10 branch fires. Otherwise the test runs at the default
// `active` interruption-level and iOS may summarise it into the
// Notification Center rather than presenting a lock-screen banner —
// leaving the user with no signal whether real medication reminders
// (which DO use time-sensitive) will surface on the lock screen.
// The title/body strings still read "Test notification" so the user
// doesn't mistake this for an actual scheduled dose.
const result = await sendViaApns(user.id, {
title: t("notifications.admin.testNotificationTitle"),
message: t("notifications.admin.testNotificationBody"),
eventType: "SYSTEM_ALERT",
eventType: "MEDICATION_REMINDER",
});
annotate({