critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

A `Notification` entity class exists with fields: id (String, non-nullable), userId (String, non-nullable), type (NotificationType enum, non-nullable), payload (NotificationPayload, non-nullable), isRead (bool, non-nullable, default false), createdAt (DateTime, non-nullable), updatedAt (DateTime, non-nullable)
A `NotificationType` enum is defined with values matching all five payload subtypes: reminder, expiry, scenario, pause, system
`Notification.fromJson` reads the `type` field first and dispatches to the correct `NotificationPayload` subtype factory (e.g., type == 'reminder' → `ReminderPayload.fromJson(json['payload'])`)
`Notification.toJson` serializes the `type` enum to its string value and calls `payload.toJson()` for the nested JSONB map
Supplying an unknown `type` string throws a descriptive `ArgumentError` or `UnknownNotificationTypeException`
copyWith allows overriding any individual field while preserving all others
Two `Notification` instances with identical field values are equal (`==` returns true, hashCodes match)
Unit tests cover fromJson round-trip for all five notification types using realistic Supabase row payloads
Unit tests verify that an unknown type string throws the expected exception
All generated code compiles without warnings after `build_runner`

Technical Requirements

frameworks
Flutter
freezed
json_annotation
build_runner
apis
Supabase row format (JSONB payload column)
data models
Notification
NotificationType
NotificationPayload
ReminderPayload
ExpiryPayload
ScenarioPayload
PausePayload
SystemPayload
performance requirements
Full Notification deserialization (including payload dispatch) must complete in under 2ms per object
security requirements
userId must never be sourced from the payload JSON itself; it must always come from the authenticated session or the Supabase row's user_id column
Avoid logging full notification payloads in production builds to prevent PII leakage

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use `@freezed` on the `Notification` class. For the type-dispatch pattern, implement a custom `JsonConverter` or handle the dispatch manually inside a `fromJson` factory: parse `type` to `NotificationType` first, then use a switch expression to call the appropriate payload `fromJson`. Keep `NotificationType` as a plain Dart `enum` with a `toJson()` extension and a static `fromString()` factory for clean error handling on unknown values. Store all files under `lib/features/notifications/domain/`.

The `Notification` class should be the only public export from the domain layer — consumers should not need to import individual payload types directly. This model will be used by both the repository (task-004) and the BLoC/Riverpod state layer, so keep it pure domain with zero Flutter/widget imports.

Testing Requirements

Unit tests in `notification_test.dart` using `flutter_test`. Required test cases: (1) fromJson for each of the five `type` values, verifying correct payload subtype is instantiated, (2) toJson round-trip producing the original map, (3) unknown type string triggers exception, (4) equality and hashCode for two identical instances, (5) copyWith produces new instance with only the changed field updated. Target 100% branch coverage for the type-dispatch switch/map. No widget or integration tests needed for this task.

Component
Notification Domain Model
data low
Epic Risks (3)
high impact medium prob technical

Supabase Realtime channels on mobile networks can drop silently. If reconnection logic is flawed, users miss notifications without knowing it, undermining the audit-trail guarantee.

Mitigation & Contingency

Mitigation: Implement exponential-backoff reconnection with a maximum of 5 retries; expose a channel-status stream to the BLoC so it can trigger a full-fetch fallback when the channel reconnects after a gap.

Contingency: If Realtime reliability proves insufficient in production, fall back to polling the repository every 60 seconds as a background supplement to the Realtime channel.

high impact medium prob security

Coordinator and org-admin RLS expansions require joining user_roles and org_memberships tables. An incorrect policy could expose notifications to wrong users or block legitimate access entirely.

Mitigation & Contingency

Mitigation: Write dedicated RLS integration tests for each role (peer mentor, coordinator, org admin) using separate Supabase test projects. Review policies with the security checklist before merging.

Contingency: If an RLS defect is discovered post-deployment, disable the expanded-scope policy and revert to user-scoped-only access while a corrected migration is prepared and tested.

medium impact medium prob integration

JSONB payload structure may vary across notification types created by different Edge Functions (reminder, expiry, scenario, pause). Missing or renamed fields will cause runtime parse failures.

Mitigation & Contingency

Mitigation: Define a canonical NotificationPayload union type in a shared schema document. Each Edge Function must validate its payload against this schema before inserting. Add fallback parsing with default values in the domain model.

Contingency: Wrap all payload parsing in try/catch and log malformed payloads to a monitoring channel; render a generic notification item rather than crashing when the payload cannot be parsed.