SummaryPeriodRepository Dart implementation
epic-periodic-summaries-foundation-task-006 — Implement the SummaryPeriodRepository class in Dart that reads from and writes to the periodic_summaries Supabase table via the service-role client. Expose methods: getSummariesForOrg(orgId, periodType), getLatestSummary(orgId, periodType), upsertSummary(SummaryPeriodModel), and deleteSummariesOlderThan(date). Ensure all queries include explicit organisation_id filters as a defence-in-depth measure beyond RLS.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
The service-role client concern is important: in a Flutter mobile app, you cannot safely embed the service-role key. The architecture should use the authenticated client for reads (RLS provides org scoping), and route writes (`upsertSummary`, `deleteSummariesOlderThan`) through a Supabase Edge Function that uses the service-role key server-side. The repository interface in Dart can still define these methods — the implementation calls the Edge Function via `supabase.functions.invoke('upsert-summary', body: model.toJson())` for writes. For MVP, if Edge Functions are not ready, use the authenticated client for writes too and rely on RLS; document this as a security debt to resolve before production.
`SummaryPeriodModel.fromJson` must use `DateTime.parse(json['period_start'] as String)` and ensure UTC: `dt.isUtc ? dt : dt.toUtc()`.
Testing Requirements
Unit tests in `test/repositories/summary_period_repository_test.dart` using `flutter_test` and `mocktail`. Mock the `SupabaseClient` query builders. Test cases: (1) `getSummariesForOrg` correctly maps response list to `List
Supabase RLS policies for aggregation views are more complex than single-table policies. A misconfigured policy could silently allow a coordinator in one organisation to see data from another, causing a data breach and breaking trust with participating organisations.
Mitigation & Contingency
Mitigation: Write automated RLS integration tests that create two separate organisations with distinct data, then assert that queries authenticated as org-A users return only org-A rows. Run these tests in CI on every PR touching the database layer.
Contingency: If an RLS bypass is discovered post-deployment, immediately disable the periodic summaries feature flag, revoke affected sessions, audit access logs, notify affected organisations, and patch the policy before re-enabling.
Activity records may span multiple sessions types, proxy registrations, and coordinator bulk entries. Incorrect JOIN logic or missing filters in the aggregation query could double-count sessions or omit activity types, producing inaccurate summaries that erode user trust.
Mitigation & Contingency
Mitigation: Build a fixture dataset covering all activity registration paths (direct, proxy, bulk) and assert expected aggregated counts in integration tests before any UI consumes the repository.
Contingency: If inaccurate counts are reported post-launch, mark affected summaries as invalidated in the database and re-trigger generation once the query is corrected. Communicate transparently to affected users via an in-app banner.
The local cache must be invalidated when a new summary arrives via push notification. If the push token is stale or the FCM/APNs delivery is delayed, the device may show an outdated summary for an extended period, confusing users who see different numbers online versus offline.
Mitigation & Contingency
Mitigation: Implement a TTL on cached summaries (max 48 hours) so stale data is auto-cleared even without a push notification. Also trigger cache refresh on app foreground if the current period's summary is older than 24 hours.
Contingency: Provide a manual pull-to-refresh on the summary card that bypasses the cache and fetches directly from Supabase when a network connection is available.