critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

validateCoordinatorPermission(coordinatorId, mentorId) returns a Success result when the mentor belongs to any chapter the coordinator manages
Returns a typed PermissionDenied failure (not a thrown exception) when the mentor is in a different chapter or not assigned to any of the coordinator's chapters
The PermissionDenied failure contains a user-displayable message in Norwegian (e.g., 'Du har ikke tilgang til å registrere aktivitet for denne likepersonen')
Permission check queries the contact_chapter table using both the coordinator's managed chapter IDs and the mentor's chapter memberships
Coordinator's managed chapter IDs are resolved from the coordinator's own contact_chapter records filtered by role (coordinator role)
A coordinator managing multiple chapters is authorized for mentors in any of those chapters
A mentor with chapter membership in chapter A cannot be registered by a coordinator who only manages chapter B
Permission check result is cached for the duration of a single registration session to avoid redundant queries (cache TTL: session-scoped, not persistent)
All Supabase query failures in the permission check are mapped to a ServiceFailure — not silently treated as permission denied
The method signature is async and returns Either<Failure, Unit> or equivalent Result type consistent with the project's error handling pattern

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15
Supabase Auth
data models
assignment
contact_chapter
contact
performance requirements
Permission check completes in under 300 ms
Use a single JOIN query rather than two sequential queries to resolve chapter overlap
security requirements
coordinatorId must be sourced from the authenticated session (auth.uid()) — never from UI input
RLS on contact_chapter must prevent a coordinator from querying chapter memberships outside their organization
Permission check must run server-side or be backed by RLS — a client-only check is insufficient
Failure message must not reveal organization structure details beyond what the coordinator already knows

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Model the permission check as a standalone method `checkProxyPermission` on `ProxyRegistrationService`, not inlined into the registration flow, so it can be tested and reused for batch operations. Use a SQL JOIN: `SELECT 1 FROM contact_chapter cm_mentor JOIN contact_chapter cm_coord ON cm_mentor.organization_unit_id = cm_coord.organization_unit_id WHERE cm_mentor.contact_id = :mentorId AND cm_coord.contact_id = :coordinatorId AND cm_coord.role_in_chapter = 'coordinator'`. Implement as a Supabase RPC call or a carefully crafted PostgREST query. The session-scoped cache can be a simple `Map` in memory, keyed by mentorId, reset when the registration flow is disposed.

Align the Either/Result type with the existing project error handling conventions established in task-002.

Testing Requirements

Unit tests with flutter_test and mocked repository. Test scenarios: (1) mentor in coordinator's chapter returns Success, (2) mentor in different chapter returns PermissionDenied with message, (3) coordinator managing multiple chapters — mentor in chapter B authorized when coordinator manages A and B, (4) Supabase error during chapter lookup returns ServiceFailure not PermissionDenied, (5) coordinatorId taken from session not parameter — verify mock session is consulted, (6) cache hit on second call for same mentor avoids second DB query.

Component
Proxy Registration Service
service medium
Epic Risks (2)
high impact low prob security

The Proxy Registration Service must verify that the coordinator has a legitimate assignment relationship with the target peer mentor before creating a record. If this check is implemented only in application code and not enforced at the DB/RLS level, a compromised or buggy client could bypass it by calling the Supabase endpoint directly, creating fraudulent proxy records for arbitrary peer mentors.

Mitigation & Contingency

Mitigation: Implement permission validation at two levels: (1) application-layer check in Proxy Registration Service that queries the assignments table before constructing the payload, and (2) RLS policy on the activities table that restricts INSERT to rows where recorded_by_user_id matches the authenticated user AND peer_mentor_id is in the set of peer mentors assigned to that coordinator. The RLS policy is the authoritative guard; the service-layer check provides early user-facing feedback.

Contingency: If RLS policy implementation is blocked by Supabase plan constraints, implement a Supabase Edge Function as a proxy endpoint that enforces the permission check server-side before forwarding to the DB. Disable direct client inserts entirely for proxy activities.

medium impact medium prob technical

For a bulk session with 30 selected peer mentors, the Proxy Duplicate Detector must query existing activities for each mentor. If implemented as 30 sequential Supabase queries, round-trip latency could make the bulk confirmation screen feel slow (>3s), degrading coordinator experience and potentially causing timeouts.

Mitigation & Contingency

Mitigation: Implement the duplicate check as a single Supabase query using an IN clause on peer_mentor_id combined with the activity_type and date filters, returning all potential duplicates for the entire batch in one network round-trip. Group results client-side by mentor ID to produce the per-mentor warning structure.

Contingency: If the single-query approach returns too much data for very large chapters, add a database index on (peer_mentor_id, activity_type, date) and profile query time. If still insufficient, accept a short loading state on the confirmation screen with a progress indicator rather than pre-loading duplicates before navigation.