high priority medium complexity backend pending backend specialist Tier 5

Acceptance Criteria

`BufdirAlignmentValidator` is a Dart service class with a single public method `validate(DateTimeRange range, String chapterId) → Future<BufdirAlignmentResult>`
`BufdirAlignmentResult` is an immutable data class containing: `Map<String, double> deltas` (field name → absolute difference), `Map<String, double> percentageDrifts` (field name → % drift), `bool isAligned` (true if all drifts ≤ 0.01%), and `DateTime validatedAt`
The validator fetches stats view totals from `StatsRepository.fetchSnapshot` and independently fetches the same period's raw totals via a Supabase RPC call that replicates Bufdir export logic — the two queries must be structurally independent
Fields compared: total_activities, total_hours, total_reimbursement_amount (all numeric fields in `StatsSnapshot` that appear in Bufdir exports)
If `percentageDrift` for any field exceeds 0.01 %, the service calls `analyticsService.logWarning('bufdir_alignment_drift', {field: ..., delta: ..., drift: ...})` with structured parameters
The validator runs in the background — it must not block any UI or data-loading flow; it is triggered after `fetchSnapshot` completes via a fire-and-forget call
Validation results are accessible via a Riverpod `StateProvider<BufdirAlignmentResult?>` for display in a debug/admin panel
When the independent RPC query fails (network error), `BufdirAlignmentResult.isAligned` is set to `false` and `analyticsService.logError` is called — the validator does not throw
Division-by-zero guard: if the stats view total is 0, percentageDrift is reported as 0.0 (not NaN or infinity)
The validator can be disabled via a feature flag (`bufdirValidationEnabled` in app config) to allow emergency bypass without a code release

Technical Requirements

frameworks
Flutter
Riverpod
supabase_flutter
apis
Supabase RPC — `rpc_bufdir_export_totals(start_date, end_date, chapter_id)` (must be created as companion task or assumed from task-004)
StatsRepository.fetchSnapshot
data models
BufdirAlignmentResult
StatsSnapshot
BufdirExportTotals (from RPC response)
performance requirements
Validation must run asynchronously — total execution time may be up to 5 seconds without affecting UX
RPC query must complete in under 3 seconds for a 12-month window on a chapter with 10 000 activities
Validator must not run more than once per 5 minutes per chapter to avoid redundant RPC load
security requirements
Validator uses the authenticated user's Supabase session — never the service role key
Drift warnings logged to analytics must not include individual activity records or PII — only aggregate deltas
Feature flag must be stored in remote config (not local), so it can be toggled without a release

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

The independence of the two queries is the entire point of this validator — if both queries use the same materialized view, drift will never be detected. The RPC function (`rpc_bufdir_export_totals`) must query the `activities` base table directly, replicating the aggregation logic from the Bufdir export pipeline, not the materialized view. This should be coordinated with task-004. Percentage drift formula: `percentageDrift = (abs(viewTotal - rpcTotal) / max(viewTotal, 1)) * 100`.

Use `max(viewTotal, 1)` as denominator to avoid division by zero while still reporting meaningful drift when viewTotal is 0 but rpcTotal is non-zero. The 5-minute rate limit should be implemented as a simple in-memory `DateTime? lastValidationRun` field on the service — no persistence needed since drift is a transient concern. Register the validator as a Riverpod provider and trigger it from the Stats BLoC's data-loading state transition.

Ensure `analyticsService.logWarning` calls include `chapter_id` (not user ID) and the reporting period for traceability without PII exposure.

Testing Requirements

Unit tests with mocked `StatsRepository` and mocked Supabase RPC client: (1) aligned case — both sources return same totals, `isAligned = true`, no analytics warning logged; (2) drift within threshold (0.005%) — `isAligned = true`, no warning; (3) drift above threshold (0.02%) — `isAligned = false`, `analyticsService.logWarning` called with correct field name and drift value; (4) RPC failure — `isAligned = false`, `analyticsService.logError` called, no exception propagated to caller; (5) zero-value stats — `percentageDrift = 0.0`, no division-by-zero; (6) feature flag disabled — validator returns immediately with `isAligned = true` without calling either data source. Integration test: seed known totals in local Supabase, run validator, assert `isAligned = true`.

Component
Stats Repository
data medium
Epic Risks (3)
medium impact medium prob technical

Materialized views over large activity tables may have refresh latency exceeding the 2-second SLA under high insert load, causing stale data to appear on the dashboard immediately after a peer mentor registers an activity.

Mitigation & Contingency

Mitigation: Design the materialized view refresh trigger to run asynchronously via a Supabase Edge Function rather than a synchronous trigger, and set a maximum staleness tolerance of 5 seconds documented in the feature spec. Add a CONCURRENTLY refresh strategy so reads are never blocked.

Contingency: If refresh latency cannot meet SLA, fall back to a regular (non-materialized) view for the dashboard and accept slightly higher query cost per request. Revisit materialized approach once Supabase pg_cron or background workers are available.

high impact medium prob integration

The aggregation counting rules for the dashboard may diverge from those used in the Bufdir export pipeline (e.g., which activity types count, how duplicate registrations are handled), creating a reconciliation burden for coordinators at reporting time.

Mitigation & Contingency

Mitigation: Run the BufDir Alignment Validator against a shared reference dataset before any view is merged to main. Encode the counting rules as a shared Supabase function called by both the stats views and the export query builder so there is a single source of truth.

Contingency: If divergence is discovered post-launch, ship a visible banner on the dashboard stating that numbers are indicative and may differ from the export until the reconciliation fix is deployed. Prioritize the fix as a P0 defect.

high impact low prob security

Multi-chapter coordinators (up to 5 chapters per NHF requirement) require RLS policies that filter on an array of chapter IDs, which is more complex than single-value RLS and could be misconfigured, leaking data across chapters or blocking legitimate access.

Mitigation & Contingency

Mitigation: Write integration tests that verify cross-chapter isolation for a coordinator assigned to chapters A and B cannot see data from chapter C. Use parameterized RLS policies with auth.uid()-based chapter lookup to avoid hardcoded values.

Contingency: If RLS misconfiguration is detected in testing, temporarily restrict coordinator queries to single-chapter scope (coordinator's primary chapter) and ship multi-chapter support as a fast-follow patch once RLS logic is verified.