Implement ActivityAttributionValidator core rules
epic-coordinator-proxy-registration-core-task-002 — Build the ActivityAttributionValidator service with the three primary validation rules: (1) attributed mentor must differ from acting coordinator, (2) mentor must be active and within the coordinator's management scope (chapter/unit), (3) duplicate detection query against the proxy_activities table within a configurable time window. Return typed ValidationResult objects.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Structure the validator with three private methods: _checkSelfAttribution (synchronous), _checkMentorScope (async, Supabase query), _checkDuplicate (async, Supabase query). Call _checkSelfAttribution first; if it fails, still proceed to _checkMentorScope and _checkDuplicate concurrently via Future.wait to collect all failures in one pass. Use Riverpod Provider to inject both a ScopeRepository and a ProxyActivityRepository (or their Supabase implementations) into the validator — never instantiate Supabase client directly inside the validator. The DuplicateDetectionConfig should default to windowStart = DateTime(date.year, date.month, date.day, 0, 0, 0).toUtc() and windowEnd = DateTime(date.year, date.month, date.day, 23, 59, 59).toUtc() computed from the request's activityDate.
Add a brief doc comment on the class explaining the dual-layer security model (client-side UX guard + server-side RLS).
Testing Requirements
Write unit tests using flutter_test with a mocked Supabase client (or a mock ProxyActivityRepository and mock ScopeRepository). Required test cases: (1) all rules pass → ValidationResult.valid(), (2) self-attribution alone fails, (3) mentor out of scope alone fails, (4) mentor inactive alone fails, (5) duplicate detected alone fails, (6) self-attribution + out-of-scope both fail in same result, (7) scope query throws SupabaseException → ValidationResult.invalid([validationQueryFailed]), (8) duplicate query throws SupabaseException → ValidationResult.invalid([validationQueryFailed]), (9) rule 1 fails → rules 2 and 3 are still evaluated (no early exit). Verify via call count mocks that rules 2 and 3 are called concurrently (both initiated before either awaits). Minimum 90% branch coverage.
The 2-hour window duplicate detection logic requires querying existing proxy records with compound key matching (mentor + date + activity type within time range). If the query is too broad it produces false positives that frustrate coordinators; if too narrow it misses genuine duplicates that corrupt Bufdir data.
Mitigation & Contingency
Mitigation: Define the duplicate detection window as a configurable parameter from the start. Prototype the Supabase query with representative data covering edge cases (midnight boundaries, different activity types same day, same activity type different mentors) before finalising the implementation.
Contingency: If the detection produces excessive false positives in UAT, allow coordinators to explicitly acknowledge and bypass the duplicate warning with a reason field, preserving safety while reducing friction.
If the proxy registration form does not clearly distinguish between the acting coordinator and the attributed mentor, coordinators may submit records attributing activities to themselves, causing inaccurate Bufdir reporting and potential funding issues.
Mitigation & Contingency
Mitigation: Conduct UAT with at least one real coordinator via TestFlight before release. Use distinct visual treatment (different card colours, explicit 'Registering on behalf of:' label) and require the confirmation screen to show both identities prominently.
Contingency: Add a mandatory confirmation checkbox on the confirmation screen that explicitly names the attributed mentor, preventing accidental self-attribution from slipping through.
Coordinators with multi-chapter access must select an active chapter context before the mentor list is filtered correctly. If chapter scope resolution fails or is bypassed, cross-org proxy registrations could occur, violating data isolation between chapters.
Mitigation & Contingency
Mitigation: Reuse the existing active-chapter-state and hierarchy-service components established by the org hierarchy feature. Add a guard that blocks entry to the proxy flow if no chapter context is active, prompting chapter selection first.
Contingency: If the chapter resolution service is unavailable, default to the most restrictive scope (no mentors visible) and surface a clear error message rather than showing an unfiltered mentor list.