high priority medium complexity integration pending integration specialist Tier 2

Acceptance Criteria

After a successful activity registration, only cache partitions matching the activity's org_id are invalidated — other org partitions are untouched
After activity deletion, the same org-scoped partition invalidation occurs
After proxy registration (coordinator registering on behalf of peer mentor), cache partitions for both the coordinator's aggregated view and the relevant peer mentor's partition are invalidated
After bulk registration, invalidation covers all affected org_id partitions in a single pass — no redundant invalidation calls
Invalidation is synchronous and completes before the mutation's success response is propagated to the UI layer
If a mutation fails (Supabase error), no cache invalidation occurs — stale cache correctly reflects the unchanged state
The invalidation contract is documented in a code comment or Dart doc on StatsCacheManager.invalidateByOrgId()
No full-cache wipe occurs for any of the above mutations — only targeted partition invalidation
An integration test confirms that a stats query after a mutation returns fresh data (cache miss) rather than stale cached data

Technical Requirements

frameworks
Flutter
Dart
Riverpod
BLoC
apis
Supabase PostgreSQL 15
data models
activity
activity_type
assignment
performance requirements
Invalidation must add <5 ms overhead to any mutation call
Bulk invalidation must not iterate the full cache — use prefix-based O(k) removal where k is the number of matching entries
security requirements
Invalidation must use the org_id from the authenticated JWT claim, not a client-supplied parameter, to prevent cross-org cache poisoning
Mutation handlers must verify the actor's role before executing — RoleAccessValidator must be called before cache invalidation

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Introduce a CacheInvalidationService (or mixin on StatsCacheManager) that exposes onActivityMutated(orgId), onBulkRegistration(List orgIds), and onProxyRegistration(orgId, peerMentorId). Each mutation use case (RegisterActivityUseCase, DeleteActivityUseCase, BulkRegisterUseCase) calls the appropriate invalidation method in its execute() body after awaiting the Supabase write. Use Riverpod's ref.invalidate() or a StreamController approach if cache consumers are Riverpod providers — this triggers automatic rebuild without manual state management. Document the contract with a Dart doc block on StatsCacheManager: '/// Callers MUST invoke invalidateByOrgId after any write that modifies activity counts or durations.'

Testing Requirements

Integration tests using flutter_test with a mocked StatsRepository: (1) register activity → subsequent cache.get() returns null for that org partition; (2) delete activity → same; (3) proxy registration → both coordinator and peer mentor partitions are invalidated; (4) bulk registration with 3 different orgs → all 3 partitions invalidated, unrelated partition untouched; (5) failed mutation → cache still returns stale hit. Unit test the invalidation routing logic independently. All tests must run without network access (mocked Supabase client).

Component
Statistics Cache Manager
data low
Epic Risks (3)
high impact medium prob technical

Pre-aggregated Supabase views may still be slow for orgs with very large activity datasets (NHF with 1,400 chapters). If the view query plan performs sequential scans, dashboard load times could exceed acceptable thresholds and degrade the perceived value of the feature.

Mitigation & Contingency

Mitigation: Design views with composite indexes on (org_id, coordinator_id, month) from the start. Run EXPLAIN ANALYZE during development against a seeded dataset of realistic scale. Add materialized view refresh strategy if needed.

Contingency: If live view performance is insufficient, convert to materialized views refreshed on a schedule or on activity-write triggers. Expose the refresh delay transparently in the UI with a 'last updated' timestamp.

high impact low prob security

Supabase RLS policies for the stats views may not be configured correctly during initial migration, potentially allowing cross-coordinator data leakage before the RoleAccessValidator layer is reached. This is a security and compliance risk.

Mitigation & Contingency

Mitigation: Write RLS integration tests as part of this epic that explicitly verify a coordinator JWT cannot read another coordinator's stats rows. Apply RLS policies in the migration script itself, not as a manual step.

Contingency: If an RLS gap is discovered post-deployment, immediately disable the stats screen via a feature flag, apply the corrected RLS migration, and re-enable after verification. Log and audit all queries that ran during the gap window.

medium impact medium prob integration

Cache invalidation logic may not be triggered correctly when a new activity is registered by a peer mentor or when an expense approval is granted. Stale data could cause coordinators to make decisions based on outdated KPIs, undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Define explicit invalidation event contracts with the activity registration and expense approval pipelines. Implement an event bus subscription within StatsCacheManager. Document the invalidation contract in code.

Contingency: If event-driven invalidation proves unreliable, add a manual 'Refresh' pull-to-refresh gesture on the dashboard and reduce TTL to 5 minutes as a fallback degradation strategy.