critical priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

MentorStatusService class is implemented as a Dart singleton or injectable service with a clean public API
pauseMentor(mentorId, reason, actorId) calls the update_mentor_status Supabase RPC and returns a typed Either<MentorStatusError, MentorStatusResult>
reactivateMentor(mentorId, reason, actorId) correctly transitions status from 'paused' to 'active' and returns a typed result
deactivateMentor(mentorId, reason, actorId) correctly transitions status from 'active' to 'inactive' and returns a typed result
getMentorStatus(mentorId) returns the current MentorStatus enum value from the database without triggering a status transition
Invalid transitions (e.g., paused→inactive, inactive→active) return a structured MentorStatusError with a clear error code and human-readable message rather than throwing an exception
All RPC calls include the actorId in the payload for audit trail purposes
The reason string is required for pause/deactivate transitions and optional for reactivate; validation enforced at method level
Network errors and Supabase client errors are caught and wrapped into MentorStatusError.networkFailure rather than propagating raw exceptions
All public methods are annotated with the assignment entity's status field constraints to ensure only valid MentorStatus enum values are used
Service handles concurrent requests safely — no race conditions on status reads before RPC call
getMentorStatus returns null safely when the mentor does not exist, with a distinct MentorStatusError.notFound variant

Technical Requirements

frameworks
Flutter
Dart
Riverpod
BLoC
apis
Supabase PostgreSQL RPC (update_mentor_status)
Supabase REST API
data models
assignment
activity
performance requirements
Each RPC call must complete within 2 seconds under normal network conditions
getMentorStatus must use a single SELECT query — no joins unless necessary
Service must not hold open database connections between method calls
security requirements
actorId must be validated against the authenticated user's JWT claims before invoking RPC
RLS policies on the assignments/mentor_status table must be enforced server-side — never rely on client-side filtering
Service role key must never be used in this mobile-facing service; only the anon/user key with RLS
reason strings must be sanitized to prevent SQL injection before transmission

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define a sealed class MentorStatusError with variants: invalidTransition, notFound, networkFailure, unauthorized. Define MentorStatus as a Dart enum with values active, paused, inactive. Use the Either pattern (from dartz or a custom Result type) as the return type for all transition methods to force callers to handle both success and failure paths. The update_mentor_status RPC should be the single authority for transition validation — do not duplicate the state machine logic in Dart; let the database CHECK constraint or RPC logic reject invalid transitions and parse the Postgres error code to determine MentorStatusError.invalidTransition.

Register MentorStatusService in the Riverpod provider tree so it can be overridden in tests. Avoid any local caching of status — always read fresh from DB to prevent stale state bugs.

Testing Requirements

Unit tests required for all four public methods covering success paths, invalid transition rejection, network failure wrapping, and null/notFound handling. Mock the Supabase client using mockito or manual test doubles. Integration test verifying the RPC call shape and parameters matches the expected Supabase function signature. Minimum 90% branch coverage on the transition logic.

Test that actorId mismatch with JWT claims produces an authorization error.

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.