ActivityAggregationRepository Dart implementation
epic-periodic-summaries-foundation-task-005 — Implement the ActivityAggregationRepository class in Dart using the Supabase client. It must call the aggregation RPCs defined in task-004, deserialise results into typed AggregationResult models, surface organisation_id in every request to enforce isolation, and propagate Supabase errors as domain exceptions. Integrate with Riverpod as an auto-dispose provider.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
The Supabase Flutter SDK's `.rpc()` method returns `PostgrestFilterBuilder` — call `.select()` then `.single()` if the RPC returns exactly one row, or handle the list and take `first`. Map the response map directly: `AggregationResult(sessionCount: response['session_count'] as int, totalHours: (response['total_hours'] as num).toDouble(), ...)`. Use `AggregationException` as a sealed class with subtypes `AggregationNetworkException`, `AggregationPermissionException`, `AggregationUnknownException` to allow BLoC to handle each case distinctly. Pass `Period` from `PeriodCalculatorService` directly — do not recompute boundaries in the repository.
Register the provider as `autoDispose` since aggregation results are session-scoped and should not persist across navigation.
Testing Requirements
Unit tests in `test/repositories/activity_aggregation_repository_test.dart` using `flutter_test` and `mockito` (or `mocktail`). Mock `SupabaseClient` and the RPC call chain. Test cases: (1) successful org aggregation RPC response is correctly deserialised into `AggregationResult`, (2) successful peer mentor RPC response is correctly deserialised, (3) `PostgrestException` is caught and rethrown as `AggregationException` with correct message, (4) network timeout throws `AggregationException`, (5) RPC returns `session_count=0, total_hours=0` (no activities) — assert `AggregationResult` reflects zeros without error. Integration test (optional): use a local Supabase instance to verify the full round-trip including RLS enforcement.
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.