critical priority medium complexity database pending backend specialist Tier 0

Acceptance Criteria

Abstract class `CertificationRepository` is defined in Dart with no concrete implementation details
Method `Future<List<Certification>> getCertificationsByMentor(String mentorId)` is declared and returns a list of certifications scoped to the given peer mentor
Method `Future<Certification> saveRenewal(Certification certification)` is declared and accepts a full Certification domain object
Method `Future<void> updateExpiryStatus(String certificationId, CertificationStatus status)` is declared with a strongly-typed CertificationStatus enum parameter
Method `Future<List<Certification>> getCertificationsExpiringWithin(Duration window, String organisationId)` is declared for batch expiry queries
Method `Future<Certification?> getCertificationById(String certificationId)` is declared for single-record lookup
CertificationStatus enum defines at least three values: valid, expiring_soon, expired
Certification domain model maps all fields from the Supabase `certification` table: id, peer_mentor_id, organization_id, cert_type, issued_at, expires_at, renewed_at, status
All method signatures return Futures to enforce async contract
Interface is placed in the domain layer, with no Supabase or Flutter imports
A `CertificationRepositoryException` base exception class is defined for repository-level errors

Technical Requirements

frameworks
Flutter
Dart
data models
certification
performance requirements
Interface must support paginated queries to avoid loading unbounded certification lists
security requirements
Interface signatures must enforce organisation scoping on all multi-record queries to prevent cross-tenant data leakage
No PII or sensitive fields exposed beyond what is required for certification lifecycle logic

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place the interface in `lib/domain/repositories/certification_repository.dart` following the clean architecture layer convention used in the codebase. The Certification domain model should be a plain Dart class (not a Supabase-specific type) to keep the domain layer portable. Use `sealed class` or `enum` for CertificationStatus so exhaustive switch handling is enforced by the Dart compiler. Avoid including `summaries`, UI hints, or presentation logic in the domain model.

The `getCertificationsExpiringWithin` method should accept a Duration rather than a raw DateTime to keep the caller logic clean — the repository implementation converts to absolute timestamps internally. Define `CertificationRepositoryException` as a base class with subclasses for `NotFound`, `PermissionDenied`, and `NetworkFailure` to enable typed error handling in the service layer.

Testing Requirements

No runtime tests are needed at this stage as this is a pure abstract interface. Write a compile-time contract test that creates a mock implementation of CertificationRepository using `mockito` or `mocktail` and asserts that all required methods are present and return correct types. Include a unit test confirming CertificationStatus enum values match the expected set (valid, expiring_soon, expired). Verify Certification domain model serialization round-trip: fromJson → toJson produces identical output.

Component
Certification Management Service
service high
Epic Risks (2)
high impact medium prob technical

The auto-pause workflow requires CertificationManagementService to call PauseManagementService and HLFDynamicsSyncService in the same logical transaction. If PauseManagementService succeeds but the Dynamics webhook fails, the mentor is paused locally but remains visible on the HLF portal.

Mitigation & Contingency

Mitigation: Implement a saga pattern: write a pending sync event to the database before calling Dynamics, and have a background retry job consume pending events. This guarantees eventual consistency even if the webhook fails transiently.

Contingency: If the Dynamics sync fails after auto-pause, surface an explicit coordinator alert in the dashboard indicating 'Dynamics sync pending — mentor may still be visible on portal'. Allow manual retry from coordinator UI.

medium impact low prob technical

If the nightly cron job runs concurrently (e.g., due to infra retry), CertificationReminderService could dispatch duplicate notifications to mentors before the cert_notification_log insert is visible to the second invocation.

Mitigation & Contingency

Mitigation: Use Supabase's upsert with a unique constraint on (mentor_id, threshold_days, cert_id) in cert_notification_log. The second concurrent insert will fail gracefully and the duplicate dispatch will be skipped.

Contingency: If duplicate notifications do reach mentors, add a post-dispatch dedup check and include a 'you may receive this notification again' disclaimer until the constraint is deployed.