Notifications — requirements
Epic. MVP-1 requirements breakdown for the Notifications local tier (see the feature architecture). Covers on-device scheduling only — permission request, per-member reminder preferences, chore-due reminders, and parent approval prompts. Each FR is sized to become one or a few user stories. Push (FCM/APNs) and milestone notifications are MVP-2/paid.
| Type | Functional |
| Layer (build approach) | Cross — local sched (free) + FCM/APNs (paid) |
| RICE | R 8 × I 2 × C 80% / E 3 = 4.3 · Tier MVP-1 (local) |
| KPI (summary) | Notification opt-in %; re-engagement taps → WAEH |
| Traces to | feature notifications · C4 containers |
| Depends on | Analytics · Account & profile management |
Success criteria (definitive KPI)
Success = ≥ 60% of activated households grant notification permission AND re-engagement:
≥ 40% of reminder_tapped events are followed by a chore_completed within the same
calendar day, sustained across weeks 2–4 post-activation.
- Metric A (opt-in rate):
notification_optin_set(granted: true)/ total activated households. Target: ≥ 60%. Measured at the household level (the account holder's device; per-member prefs are downstream of the grant). A rate below 50% in the first cohort signals a permission-UX problem, not a product-fit problem. - Metric B (re-engagement rate):
chore_completedevents preceded by areminder_tappedon the same calendar day / totalreminder_tappedevents. Target: ≥ 40%. Window: weeks 2–4 post-activation (activation = first approved completion); weeks 2–4 exclude the novelty effect of week 1. - Guardrail (frequency erosion): if
reminder_tappedvolume drops > 30% week-over-week without a corresponding opt-out, investigate scheduling drift or quiet-hours misconfiguration — a health signal, not a tunable target. - Baseline: no install base yet — treat 60% / 40% as the launch hypothesis; re-baseline from the first cohort's week-2 data. 60% opt-in is informed by ADHD/neurodiversity app benchmarks where reminders are a primary stated need; 40% same-day completion tap-through is a conservative floor given functional-only content.
Analytics — events to record
All events are INTERNAL BI — content-free, no child identity, no chore content, never marketing (see COPPA and the three-bucket model). Never emit a promotional or marketing event triggered by a child's action.
| Event | When | Key properties | Feeds |
|---|---|---|---|
notification_optin_set | Platform permission dialog resolves | granted (bool), platform (ios|android) | KPI-A numerator; per-platform opt-in health |
reminder_scheduled | A chore-due reminder is written to the local scheduler | scheduledCount (coarse: 1|2-5|6+), hasQuietHours (bool) | Scheduling health; drift audit |
reminder_tapped | User taps a local notification; app foregrounds | notificationType (chore_due|approval_request) | KPI-B denominator; re-engagement funnel |
approval_prompt_tapped | Account holder taps an approval-request notification | (no additional props — chore identity must not be logged) | Approval-funnel latency signal |
reminder_tappedis the KPI-B denominator; the numerator is achore_completedevent (owned by Today) on the same calendar day. The cross-KPI join is BI-side (no person identity in either event). Both events must be live before KPI-B is measurable — instrument together, not in sequence.
Scope
Notifications MVP-1 is the on-device local tier: the permission grant flow, per-member reminder on/off and quiet-hours preferences, chore-due reminders scheduled from local device state, and approval-prompt notifications delivered to the account holder when a child submits a chore. Notifications are functional only — they carry no chore content, no promotional copy, and are never delivered to a child as marketing. Reminders sent to a child's device are functional task prompts only (chore due soon) and require the account holder's prior notification grant.
Functional requirements
FR-NOTIFY-1 — Notification permission request
Priority: P1 · Status: 🔨 to build Statement. As the account holder, I am prompted to grant notification permission at an appropriate moment in onboarding so reminders can be delivered on this device. Acceptance
- Given a fresh install with no prior permission decision When the in-app
pre-prompt appears Then it explains the value (functional reminders, never spam,
can be turned off anytime) before the platform dialog is shown;
notification_optin_setfires on resolution regardless of outcome. - Given permission is denied When I navigate to Settings → Notifications Then a clear nudge links to platform settings; the in-app dialog is never re-shown without me initiating.
- Given permission is granted Then the scheduler activates; Given it is denied or subsequently revoked Then no local notifications are scheduled and the preference UI reflects the disabled state.
FR-NOTIFY-2 — Per-member reminder preferences
Priority: P1 · Status: 🔨 to build Statement. As the account holder, I can enable or disable chore-due reminders per household member and set quiet hours so notifications respect each member's schedule. Acceptance
- Given a household member When I open their notification preferences Then I
can toggle reminders on/off and set a quiet-hours window (start time, end time);
reminder_scheduledreflectshasQuietHours: truewhen a window is set. - Given quiet hours are configured When a reminder would fire inside the window Then it is suppressed without rescheduling.
- Preferences persist locally across restarts and sync to Supabase when online so they survive reinstalls on the same account.
FR-NOTIFY-3 — Chore-due reminder scheduling (local, on-device)
Priority: P1 · Status: 🔨 to build Statement. As a household member, I receive a local on-device reminder when a chore assigned to me is due soon so I am prompted to act before missing the window. Acceptance
- Given a chore with a due time When reminder scheduling runs Then a local
notification fires ≈ 30 minutes before the due time (configurable default);
reminder_scheduledemits. - Given the chore is completed or removed before the notification fires Then the scheduled notification is cancelled.
- Given the device is offline When the reminder fires Then it still delivers (local scheduling requires no network); tapping it opens the Today surface.
- Notification copy is content-minimal (e.g. "You have a chore due soon") — no chore name, no token amount, no member name in the payload.
FR-NOTIFY-4 — Parent approval-request prompt
Priority: P1 · Status: 🔨 to build Statement. As the account holder (parent), I receive a local notification when a child submits a chore for approval so I can review it promptly. Acceptance
- Given a chore submission enters
submittedstate When the scheduler evaluates it Then an approval-request notification is delivered to the account holder's device within 60 seconds;approval_prompt_tappedfires when tapped. - Given the submission is already approved or rejected before the notification is tapped Then tapping it routes to the approval queue (empty/acted-state) without error.
- Notification copy is content-minimal (e.g. "A chore is waiting for your review") — no child name, no chore name in the payload.
FR-NOTIFY-5 — Notification tap routes to the correct surface
Priority: P2 · Status: 🔨 to build Statement. As a user, tapping a notification brings me to the relevant in-app surface so I can act without searching. Acceptance
- Given a
chore_duenotification When tapped Then the app opens to the Today surface. - Given an
approval_requestnotification When tapped Then the app opens to the approval queue. - Given the app is backgrounded or terminated When the notification is tapped Then the app cold-starts to the correct route via the notification type tag only — no chore id or member id in the payload; routing is type-only.
Architecture considerations
- One data path —
NotificationsBloc → NotificationsRepository → client_sdk facade → NotificationsService → LocalNotificationsAdapter. No bloc or page importsflutter_local_notificationsdirectly; the adapter is the sole I/O boundary. - Scheduler in the SDK service —
NotificationsServiceowns scheduling logic (timing, quiet-hours evaluation, cancellation on completion);LocalNotificationsAdapterwrapsflutter_local_notifications(or equivalent) and is the only surface that touches the platform plugin. - Content-free payloads — notification payloads carry only a type tag (
chore_due/approval_request) and a route hint; no chore id, member id, name, or token amount enters the notification channel. This satisfies the minor-safe pre-emit filter structurally — there is nothing to strip at runtime. - Offline-first — local reminders are scheduled on-device and fire without a network connection. The scheduler hydrates from the Drift write-through cache (the same offline store powering Today), so reminders survive airplane mode.
- Cancellation consistency — when a
chore_completedorcompletion_approvedevent lands (via the Today offline queue or a live sync),NotificationsService.cancelForChoreis called; the adapter cancels any pending local notification to prevent stale reminders. - Push deferred — no FCM/APNs token is registered in MVP-1. The
NotificationsAdapterinterface is designed to be swapped for aPushNotificationsAdapterin MVP-2 without changing the service or bloc. - Household-scoped RLS — notification preferences are stored in a
notification_preferencestable scoped to the household's RLS policy; no cross-household reads are possible.
Design work (ahead of build)
- Pre-prompt / permission UX — the in-app pre-prompt before the platform dialog; must state clearly "functional reminders only, never promotional, you can turn off at any time." Required on both iOS (always) and Android 13+ (POST_NOTIFICATIONS explicit grant).
- Notification preference surface — a per-member prefs card in Settings → Notifications: toggle, quiet-hours time-range picker, and a visible "notifications off" state (when permission is denied) with a link to platform settings.
- Notification copy — functional, content-minimal strings for
chore_dueandapproval_request; must omit child name, chore name, and token amount. Copy should be warm but brief (≤ 8 words per notification). - Approval-prompt acted state — the approval-queue view when the submission was already reviewed before the parent taps the notification; must not show a confusing blank screen.
Decisions (resolved for MVP-1)
Resolved — see the MVP-1 decisions log for the canonical record, rationale, and status legend (✅ decided · ⚖️ counsel confirms · 🔜 MVP-1.x).
- D-NOTIF-1 — Chore-due lead time. ✅ Default 20 min (ADHD-leaning) + a per-household preference (15 / 20 / 30); A/B post-launch.
- D-NOTIF-2 — Approval-prompt batching. ✅ One summary notification with a count ("3 chores need approval") → taps into the approval queue, which preserves per-item context.
- D-NOTIF-3 — Kid-mode suppression. ✅ Chore-due reminders fire in kid-mode (they help the child); approval prompts do not (parent-only). Decided now so the scheduler is correct when kid-mode (MVP-2) lands.
- D-NOTIF-4 — Quiet-hours default. ✅ Ship a sensible default window 21:00–07:00, user-editable / disable — avoids night-time noise out of the box.
Out of scope (MVP-1)
- Push notifications (FCM/APNs) — cross-device delivery via a push provider → MVP-2/paid;
the
NotificationsAdapterinterface is designed to support this without service changes. - Milestone notifications (goal reached, kudos received) → MVP-2; the goal-loop KPI is tracked via the Member Profile epic, not the local scheduler.
- Promotional / marketing notifications — never to children under any tier; parent-only lifecycle communications (if any) are a marketing-bucket concern routed through Email digests, not the local notification tier.
- Teen (13–17) consent nuances for push — deferred alongside push; the local tier's permission is a standard platform grant tied to the adult account holder.
- Daily or weekly digest notifications — a summary of household activity is a separate product decision; not MVP-1.