high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

The `AlignmentReport` object has a `formattedSummary` String field containing human-readable diff text
When discrepancies exist, `formattedSummary` contains lines in the format: 'Dashboard shows N activities for type [name], export aggregation shows M' for each discrepancy
When no discrepancies exist, `formattedSummary` is 'All activity type totals match between dashboard and export paths.'
`AlignmentReport.isPassing` is a boolean that CI can assert against — false triggers CI test failure
A `runValidation` or `triggerValidation` method is exposed that wraps `validate()` and catches all exceptions, returning a failed report rather than throwing
The validator is accessible from a hidden developer settings menu (e.g., tapping a version number 7 times or via a dedicated dev screen)
The developer menu integration does not affect production users — guarded by `kDebugMode` or a build flavor flag
The method can be called directly from integration tests without any UI interaction
The structured `AlignmentReport` is serialisable to JSON for CI output parsing
At least one complete round-trip call (trigger → validate → format → return) completes without throwing in a test environment

Technical Requirements

frameworks
Flutter
Riverpod
apis
Supabase PostgreSQL 15 (via Supabase SDK)
data models
activity
activity_type
bufdir_column_schema
bufdir_export_audit_log
performance requirements
Full validation round-trip must complete within 10 seconds on a typical device with real Supabase connection
Formatted summary generation is synchronous and must not block the UI thread
security requirements
Developer trigger endpoint must not be reachable from the production app's navigation graph
AlignmentReport JSON output must not include raw PII — only activity type names and aggregate counts
CI invocation must use a test-scoped Supabase service role, not production credentials

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Add a `toFormattedSummary()` method on `AlignmentReport` that iterates `discrepancies` and uses string interpolation: `'Dashboard shows ${d.dashboardTotal} activities for type ${d.activityTypeName}, export aggregation shows ${d.exportTotal}'`. Implement `toJson()` on `AlignmentReport` and `AlignmentDiscrepancy` for CI consumption. For the developer menu, create a `DevToolsScreen` widget (only compiled in debug builds via `kDebugMode` guard) with a button that calls `runValidation()` and displays the `formattedSummary` in a scrollable dialog. For CI, expose a Flutter integration test that instantiates the validator with real (test-env) repos and asserts `report.isPassing`.

Do not use `debugPrint` as the primary output — return structured data that callers can format as needed.

Testing Requirements

Write unit tests: (1) given a passing AlignmentReport, formattedSummary equals the expected passing message; (2) given a report with 2 discrepancies, formattedSummary contains exactly 2 lines with correct values and names; (3) given an exception during validate(), runValidation() returns a failing AlignmentReport with error field set rather than throwing; (4) AlignmentReport.toJson() produces parseable JSON with isPassing, discrepancies, and formattedSummary fields. Include one integration test that calls runValidation() with mock repos returning known divergent data and asserts the structured output.

Component
Bufdir Alignment Validator
service medium
Epic Risks (2)
medium impact medium prob technical

Supabase realtime channel subscriptions that are not properly disposed on screen close can accumulate in memory across navigation events, causing duplicate invalidation calls, ghost fetches, and eventual memory leaks on long sessions.

Mitigation & Contingency

Mitigation: Implement StatsCacheInvalidator as a Riverpod provider with an explicit ref.onDispose callback that cancels the realtime channel subscription. Write a widget test that navigates away and back multiple times and asserts that only one subscription is active at any given time.

Contingency: If subscription leaks are found in production, add a global subscription registry that enforces at-most-one subscription per channel key, and schedule a dispose sweep on app background events.

medium impact low prob scope

Debouncing rapid inserts may swallow the invalidation signal if the debounce window outlasts the Supabase realtime event delivery window, resulting in the dashboard showing stale totals after a bulk registration completes.

Mitigation & Contingency

Mitigation: Set the debounce window to 800ms (shorter than the typical Supabase realtime delivery latency of 1-2s for batched events) and ensure the leading-edge invalidation fires immediately while trailing duplicates are suppressed. Integration-test with a 20-record bulk insert.

Contingency: If debounce timing proves unreliable, replace debounce with a trailing-edge timer reset on each event and add a guaranteed invalidation 5 seconds after the last event regardless of subsequent events.