critical priority high complexity backend pending backend specialist Tier 2

Acceptance Criteria

Service exposes a `generateSummary({required String orgId, required PeriodType periodType, DateTime? referenceDate})` method that returns a `SummaryGenerationResult`
Service calls OrgIsolationGuard at entry — a missing or invalid orgId throws `OrgIsolationException` before any downstream calls
Service uses PeriodCalculatorService to derive `currentPeriod` and `previousPeriod` from the reference date (defaults to `DateTime.now()` if not provided)
Service fetches current-period and previous-period aggregates from ActivityAggregationRepository and computes `deltaCount` and `deltaPercentage` fields in the result
Service invokes OutlierDetectionService and includes the resulting `OutlierReport` in the `SummaryGenerationResult`
Generated summary is persisted to the `summary_periods` table via the SummaryPeriodRepository before triggering notifications
Service triggers PushNotificationDispatcher with a `summary-ready` event after successful persistence — notifications are not sent if persistence fails
Service run is idempotent: if a summary already exists for the same org + period, the service updates the existing record rather than creating a duplicate
Service emits structured progress events (`SummaryGenerationProgress`) at each stage (started, aggregation_complete, outliers_complete, persisted, notifications_dispatched) for UI progress tracking
A partial failure in notification dispatch does not roll back the persisted summary — it is logged and surfaced in `SummaryGenerationResult.warnings`
Service completes a full generation run for an organisation with 500 peer mentors and 5,000 activities within 30 seconds
All downstream service calls use injected dependencies (no direct instantiation) to support unit testing

Technical Requirements

frameworks
Flutter
Supabase Dart SDK
Riverpod
BLoC (for progress stream consumption in UI layer)
apis
Supabase REST API (summary_periods, outlier_reports tables)
Push notification via PushNotificationDispatcher abstraction
data models
SummaryPeriod
ActivityAggregateResult
OutlierReport
SummaryGenerationResult
SummaryGenerationProgress
Organisation
PeerMentor
performance requirements
Full generation run completes within 30 seconds for 500 peer mentors / 5,000 activities
Aggregation and outlier detection must be invoked with pre-fetched data — no redundant Supabase queries
Progress events must be emitted within 500ms of each stage completing to keep UI responsive
security requirements
OrgIsolationGuard must be the first call in `generateSummary` — no data access before guard validation
SummaryGenerationResult must not include raw activity content — only aggregate metrics
Persist summary with `organisation_id` field set; RLS ensures only the correct org can read it
Idempotency check must use a unique constraint on (organisation_id, period_type, period_year) to prevent race conditions

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Design this as a pure orchestrator — it contains no business logic itself, only the sequence of calls to injected services. Use the Facade pattern: `SummaryGenerationService` is the single public entry point; all dependencies are injected via constructor and typed as interfaces/abstract classes for mockability. Expose progress as a `Stream` using a `StreamController` so the BLoC layer can map progress events to UI states without polling. For idempotency, perform an upsert (Supabase `upsert` with `onConflict`) on `summary_periods` keyed on `(organisation_id, period_type, period_year, period_half_or_quarter)` — this is atomic and avoids a read-before-write race.

Notifications must be triggered only after the upsert confirms success (check Supabase response status). Structure `SummaryGenerationResult` as a sealed class with `SummaryGenerationSuccess` and `SummaryGenerationFailure` subtypes to force callers to handle both cases explicitly. The `warnings` field on the success type carries non-fatal issues (e.g., notification partial failures, missing thresholds for outlier detection). This service is the integration point for the 'Spotify Wrapped'-style gamification feature described in the workshop notes — design the `SummaryPeriod` data model to be extensible for per-peer-mentor summary records in a future phase.

Testing Requirements

Unit tests with flutter_test using fully mocked dependencies (ActivityAggregationRepository, PeriodCalculatorService, OutlierDetectionService, PushNotificationDispatcher, SummaryPeriodRepository): verify correct orchestration sequence; verify delta computation (positive, negative, zero-delta cases); verify idempotency (second call with same org/period updates rather than inserts); verify that notification failure does not roll back persistence; verify progress events are emitted in correct order; verify OrgIsolationException is thrown before any mock is called when orgId is invalid. Integration tests: run full generation against a Supabase test project with seeded data and assert `summary_periods` record is created with correct metrics and that the outlier report is linked. End-to-end test via TestFlight: trigger a generation run from the coordinator UI and verify the push notification arrives and deep-links to the correct summary screen. Minimum 90% branch coverage on the orchestration logic.

Component
Summary Generation Service
service high
Dependencies (5)
Build the service that computes half-year and quarterly period boundaries from a reference date, identifies the current active period, determines the previous period for year-over-year delta computation, and validates that a given date falls within an expected period window. epic-periodic-summaries-core-logic-task-005 Implement the push notification dispatcher service responsible for constructing and dispatching personalised digest notifications to both FCM (Android) and APNs (iOS) platforms. Must support deep-link routing payloads, notification payload construction for summary-ready events, and delivery confirmation tracking. epic-periodic-summaries-core-logic-task-006 Build the service that classifies peer mentors as outliers based on configurable thresholds for activity volume. Must support underactive detection (below minimum activity threshold), overloaded detection (above maximum threshold), organisation-scoped evaluation runs, and structured outlier reports surfaced to coordinators. epic-periodic-summaries-core-logic-task-007 Build the data access layer for summary periods, including CRUD operations for period metadata, period boundary calculations storage, and idempotency key management to prevent duplicate summary generation runs. epic-periodic-summaries-core-logic-task-001 Implement the repository responsible for querying and aggregating raw activity records across a given period and organisation scope. Must support half-year and quarterly window queries, year-over-year comparisons, and organisation-level data isolation. epic-periodic-summaries-core-logic-task-003
Epic Risks (4)
high impact medium prob technical

Supabase pg_cron or Edge Function retries could trigger multiple concurrent generation runs for the same period and organisation, producing duplicate summaries and sending multiple push notifications to users — a serious UX regression.

Mitigation & Contingency

Mitigation: Implement a database-level run-lock using an INSERT … ON CONFLICT DO NOTHING pattern keyed on (organisation_id, period_type, period_start). Only the first successful insert proceeds; subsequent attempts read the existing lock and exit early. Test with concurrent invocations in a Deno test suite.

Contingency: If duplicate summaries are detected post-deployment, add a deduplication cleanup job that removes all but the most recent summary per (user_id, period_type, period_start) and sends a corrective push notification.

medium impact low prob integration

FCM and APNs have different payload structures and size limits. An oversized or malformed payload could cause silent notification drops on iOS or delivery failures on Android, meaning mentors never learn their summary is ready.

Mitigation & Contingency

Mitigation: Build the PushNotificationDispatcher with separate FCM and APNs payload constructors, enforce a 256-byte body limit on the preview text, and run integration tests against the Firebase Emulator and a test APNs sandbox.

Contingency: Fall back to a generic 'Your periodic summary is ready' message if personalised preview text construction fails, ensuring delivery even when the personalisation pipeline encounters an error.

medium impact high prob scope

Outlier thresholds that are too tight will flag most mentors as outliers (alert fatigue for coordinators), while thresholds that are too loose will miss genuinely underactive mentors — directly undermining HLF's follow-up goal.

Mitigation & Contingency

Mitigation: Implement thresholds as configurable per-organisation database settings rather than hardcoded constants. Provide sensible defaults (underactive < 2 sessions/period, overloaded > 20 sessions/period) and document the tuning process for coordinators in the admin portal.

Contingency: If coordinators report threshold miscalibration after launch, expose a threshold configuration UI in the coordinator admin screen and allow real-time threshold adjustment without requiring a code deployment.

low impact high prob scope

The app may not have 12 months of historical activity data for all organisations at launch, making year-over-year comparison impossible for most users and rendering the comparison widget empty, which could disappoint users expecting Wrapped-style insights.

Mitigation & Contingency

Mitigation: Design the generation service to gracefully handle missing prior-year data by setting the yoy_delta field to null rather than zero. The UI must treat null as 'no comparison available' with appropriate placeholder copy rather than showing a misleading 0% delta.

Contingency: If historical data import from legacy Excel/Word sources becomes feasible, add a one-time backfill Edge Function that populates prior-year activity records from imported spreadsheets. Until then, explicitly communicate the data-availability limitation in the first summary each user receives.