Build Summary Generation Service Core
epic-periodic-summaries-core-logic-task-008 — Implement the central orchestration service that drives an organisation-scoped summary generation run: fetches aggregated activity data, computes metrics and year-over-year deltas, invokes the outlier detection service, persists results via the summary period and cache repositories, and triggers push notifications upon completion.
Acceptance Criteria
Technical Requirements
Execution Context
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
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.
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.
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.
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.
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.