critical priority high complexity backend pending backend specialist Tier 2

Acceptance Criteria

The `aggregateActivities` method accepts `ScopeLevel.region` and `ScopeLevel.national` in addition to `ScopeLevel.chapter` without throwing an error
For `ScopeLevel.region`: the org hierarchy resolver is called with the region's org_id and returns the list of all chapter org_ids subordinate to that region; the aggregation query then runs across all those chapter IDs
For `ScopeLevel.national`: the org hierarchy resolver returns all chapter org_ids across all regions under the national organisation; the aggregation query covers all of them
The returned `BufdirPayload` includes a `BufdirOrgRollUpMetadata` field populated with the list of contributing chapter org_ids and the contributing_chapter_count
A region-scope query for NHF (which has up to 9 regions and ~1,400 chapters) completes in under 5 seconds for a one-year reporting period
A national-scope query completes in under 10 seconds for a one-year reporting period (benchmark against realistic seed data)
Cross-org data leakage is impossible: the set of chapter org_ids returned by the hierarchy resolver is intersected with the org_ids the caller's JWT grants access to before executing the query; any org_id not in the JWT's allowed set is silently excluded and logged at warning level
If the hierarchy resolver returns zero subordinate chapters for a region or national scope (e.g., a newly created region with no chapters), the method returns an empty BufdirPayload with contributing_chapter_count = 0 rather than an error
The org hierarchy resolver function is unit-tested independently with mocked Supabase responses covering: single-chapter region, multi-chapter region, empty region, and full national hierarchy
No N+1 query pattern: the multi-unit aggregation uses a single SQL IN clause or equivalent batch query, not a separate query per chapter

Technical Requirements

frameworks
Dart (latest null-safe)
supabase_flutter
Supabase RPC or Edge Function for hierarchy resolution and multi-org aggregation
flutter_test + mocktail
apis
Supabase PostgREST API (organisations table for hierarchy, activities table for aggregation)
Supabase RPC (`get_subordinate_org_ids`, `aggregate_activities_for_orgs`)
data models
BufdirPayload
BufdirOrgRollUpMetadata
BufdirPeerMentorRecord
Organisation (hierarchy structure with parent_org_id)
ScopeLevel (enum)
performance requirements
Region-scope query under 5 seconds for a one-year period
National-scope query under 10 seconds for a one-year period
Use SQL IN clause for multi-org batch query, not per-chapter queries
Index on organisations.parent_org_id required for hierarchy traversal
security requirements
JWT-based org access scope must be validated before executing any multi-org query — callers cannot request hierarchies above their authorised level
RLS must prevent activities from leaking to unauthorised callers even if org_ids are guessed
The hierarchy resolver must be idempotent and not cache org_ids across user sessions

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Implement the org hierarchy resolver as a separate Dart class (`OrgHierarchyResolver`) with a single method `getSubordinateOrgIds({required String rootOrgId, required ScopeLevel scope})`. This makes it independently testable and reusable by other features. For the hierarchy query, use a recursive SQL CTE (`WITH RECURSIVE`) in a Supabase RPC function rather than loading the entire org table and traversing it in Dart — this is far more efficient for NHF's 1,400-chapter structure. The Dart service class calls the RPC to get the ID list, then calls the aggregation RPC with the list.

Keep RPC payloads small: pass only org_id lists, not full org objects. For the JWT intersection security check, extract `allowed_org_ids` from the JWT claims (or query the user's org memberships) and compute the intersection with the hierarchy resolver's output using a Dart Set operation before proceeding. Log any excluded IDs at warning level for audit purposes.

Testing Requirements

Unit tests: mock the org hierarchy resolver to return a controlled set of chapter IDs and verify the aggregation query is built with the correct IN clause. Test all three scope levels in a single test suite. Test the security boundary: pass a chapter-level user's JWT and request region scope — confirm the method only aggregates chapters the JWT allows. Integration tests (local Supabase): seed three chapters under one region, one chapter under a different region, and a national org containing both regions.

Assert that a region-scope query returns only the three chapters, not the isolated one, and national returns all four. Performance test: seed 1,400 chapter IDs and 10,000 activity rows across them; assert the national-scope query returns within the 10-second threshold.

Component
Activity Aggregation Service
service high
Epic Risks (3)
high impact medium prob technical

Supabase Edge Functions have a default execution timeout. For large national-scope exports aggregating tens of thousands of activities across 1,400 chapters, the edge function may time out before completing, leaving coordinators with a failed export and no partial output.

Mitigation & Contingency

Mitigation: Optimise the aggregation SQL using pre-materialised aggregation views or RPC functions that run inside the database rather than iterating records in Deno. Profile query execution time against realistic production data volumes early. Request an elevated timeout limit from Supabase if needed. Implement progress checkpointing so the export can be resumed from the last completed aggregation batch.

Contingency: For organisations exceeding a configurable threshold (e.g. >5,000 activities), switch to an asynchronous export pattern: the edge function writes a 'pending' audit record and enqueues the job; the client polls for completion and is notified via Supabase Realtime when the file is ready.

medium impact medium prob technical

Server-side PDF generation in a Deno Edge Function environment restricts library choices. Many popular PDF libraries require Node.js APIs not available in Deno, or produce large bundle sizes that exceed edge function limits. Choosing the wrong library could block the entire PDF generation path.

Mitigation & Contingency

Mitigation: Spike PDF library selection as the first task of this epic, evaluating at least two Deno-compatible options (e.g. pdf-lib, jsPDF with Deno compatibility shim). Test bundle size and basic rendering before committing to an implementation. Document the chosen library's constraints.

Contingency: If no suitable Deno-native PDF library is found, generate a well-structured HTML report from the edge function and use a headless Chromium service (e.g. Browserless, Gotenberg) for HTML-to-PDF conversion, or temporarily ship CSV-only export while the PDF path is resolved.

high impact high prob technical

Peer mentors affiliated with multiple chapters (a documented NHF scenario) must not be double-counted in participant totals. Incorrect deduplication logic would overreport participation figures to Bufdir, which could be discovered during audit and damage organisational credibility.

Mitigation & Contingency

Mitigation: Define and document the deduplication contract explicitly before coding: deduplication is per-person per-period, not per-activity. Build dedicated unit tests with fixtures containing the exact multi-chapter membership patterns described in NHF's documentation. Have a NHF representative validate test fixture outputs against known-good manual counts.

Contingency: If deduplication logic produces results that cannot be verified against manual counts before launch, surface a deduplication warning in the export preview listing the affected peer mentor IDs, and require explicit coordinator acknowledgement before finalising the export.