high priority low complexity backend pending backend specialist Tier 3

Acceptance Criteria

getMentorsByStatus(MentorStatus.active) returns only mentors with status 'active' within the calling user's organization scope
getMentorsByStatus(MentorStatus.paused) returns only paused mentors, correctly excluding active and inactive mentors
When chapterId is provided, results are further filtered to that specific chapter — mentors from other chapters are not returned
When chapterId is omitted, the method returns mentors across all chapters the calling user has access to (governed by RLS)
Returned list contains at minimum: mentor_id, display_name, chapter_id, current_status, and certification_expiry_date
Empty result set is returned as an empty list, not null or an error
Method returns Either<MentorStatusError, List<MentorSummary>> — network and auth errors are wrapped correctly
The Supabase query uses .eq('status', status.name) and optionally .eq('chapter_id', chapterId) — no client-side post-filtering of results
RLS on the underlying table correctly prevents a coordinator from seeing mentors outside their organization
Query execution time is under 500ms for organizations with up to 500 mentors

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase REST API (select query with eq filters)
data models
assignment
contact
performance requirements
Query must use indexed status and chapter_id columns — confirm index exists before deployment
Method must not fetch full mentor profiles — only the fields needed for assignment pool filtering
Under 500ms response for 500-mentor organizations on a standard Supabase plan
security requirements
RLS must be the authoritative scope boundary — do not add a client-side organization_id filter as the sole guard
The chapterId parameter must be validated against the JWT claims to ensure the user has access to that chapter
No sensitive PII (personnummer, health data) included in the returned MentorSummary model

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Define a MentorSummary value object (Dart class with copyWith and equality) containing only the fields needed for assignment pool logic. Avoid fetching the full contact or assignment record. Use Supabase's select('mentor_id, display_name, chapter_id, status, certification_expiry_date') to minimize payload size. The chapterId parameter should be typed as String?

and handled with a conditional .eq() call rather than a ternary on the query builder — this keeps the query construction readable. Consider adding a convenience method getMentorsAvailableForAssignment() that internally calls getMentorsByStatus(MentorStatus.active) — this makes the intent explicit at the call site in the assignment pool logic.

Testing Requirements

Unit tests with mocked Supabase client verifying: correct .eq filter applied for each status value, chapterId filter is conditionally applied when provided and omitted when null, empty list returned when no results, error propagation for network failures. Integration test (or widget test with supabase_flutter mock) confirming query shape. Verify that the RLS correctly scopes results by running queries as different test users belonging to different chapters.

Component
Mentor Status Service
service medium
Epic Risks (3)
medium impact low prob technical

The status state machine must handle race conditions where two concurrent callers (e.g., a mentor self-pausing and a coordinator force-pausing simultaneously) attempt to update the same mentor's status. Without a concurrency guard, both writes could succeed, leaving the audit log in an inconsistent state.

Mitigation & Contingency

Mitigation: Use a Supabase RPC with a row-level lock (SELECT FOR UPDATE) inside a transaction so only one transition wins. Return a clear error to the losing caller. Test with concurrent requests in the integration test suite.

Contingency: If row-level locking proves unreliable in the Supabase environment, add an optimistic-locking version field to peer_mentors and have the service retry up to three times on version conflict before surfacing an error to the caller.

high impact medium prob technical

If the CertificationExpiryJob Edge Function fails silently (network timeout, Supabase cold start), HLF mentors with expired certifications could remain in active status and continue appearing on the chapter website, creating a compliance breach.

Mitigation & Contingency

Mitigation: Implement structured error logging inside the Edge Function, write a monitoring query that checks for mentors with expired certifications still in active status, and set up an alert if any are detected 30 minutes after the scheduled nightly run.

Contingency: Provide a coordinator-accessible manual trigger for the expiry check that can be invoked via the admin interface if the scheduled job is known to have failed. Document the manual recovery procedure for HLF coordinators.

medium impact medium prob dependency

pg_cron registration in Supabase requires superuser-level access that may not be available in all environments (local dev, staging, CI). If the cron job cannot be registered automatically, the Edge Function will never execute on schedule, breaking the HLF certification expiry workflow.

Mitigation & Contingency

Mitigation: Use Supabase's recommended pg_cron setup via the SQL editor migration script and document the exact commands. Validate cron registration in the staging environment as part of the epic's deployment checklist.

Contingency: If pg_cron is unavailable, switch to a Supabase scheduled Edge Function invocation via an external cron service (e.g., a GitHub Actions scheduled workflow calling the Edge Function endpoint with a service-role key) until the pg_cron approach is resolved.