critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

CertificationRecordRepository class exists under lib/features/peer_mentor/data/repositories/ and is registered as a Riverpod provider
fetchCertificationHistory(String mentorId, String organizationId) is an async method returning Future<List<CertificationRecord>?>
Method returns null (not an empty list) when the organizationId does NOT belong to the set of certification-enabled organizations — null semantically means 'certifications not applicable here'
Method returns List<CertificationRecord> (possibly empty) when the organizationId belongs to a certification-enabled organization (e.g., HLF) — an empty list means 'applicable but no records yet'
The certification-enabled organization set is determined by a configuration source (e.g., a CertificationConfig provider, a feature flag, or an org metadata field from Supabase) — NOT by hardcoding 'HLF' as a string in the repository
For certification-enabled orgs, method queries the certification_records table filtered by mentor_id and ordered by issued_at descending
Results are mapped to List<CertificationRecord> via CertificationRecord.fromJson()
Throws RepositoryException for Supabase errors on certification-enabled org queries
Unit tests cover: null return for non-HLF org, empty list for HLF org with no records, populated list for HLF org with records, RepositoryException on Supabase error for HLF org

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase PostgREST — certification_records table SELECT filtered by mentor_id
Organization metadata source for certification feature flag
data models
CertificationRecord
PeerMentorProfile (for org context)
performance requirements
For non-certification orgs, no Supabase call is made — return null immediately to avoid unnecessary network round-trips
Certification record fetch should select all relevant fields but avoid joining large related tables
security requirements
The organizationId used for branching must come from the authenticated session context — do not accept it as a user-provided parameter without validation against the session
Certification records may contain sensitive training and health-adjacent data — ensure RLS on certification_records restricts access to authorized roles

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

The null vs empty list distinction is the most important aspect of this method — document it clearly with a doc comment on the method signature: /// Returns null if certifications are not applicable for the given organization. /// Returns an empty list if applicable but no records exist. The org branching should consult an injected ICertificationConfigProvider (or equivalent) that returns a Set of organization IDs where certifications are enabled. This config can be seeded from Supabase org metadata, a remote config, or a local constant during early development — the key is that the repository is decoupled from hardcoded strings.

The UI consuming this method should render a 'Certifications not available for this organization' message when null is returned, and 'No certifications yet' when an empty list is returned — these are two distinct UI states. Order results by issued_at descending so the most recent certification appears first.

Testing Requirements

Unit tests using flutter_test with mocked SupabaseClient and mocked certification config provider. Test file: test/features/peer_mentor/data/repositories/certification_record_repository_test.dart. Required cases: (1) returns null without calling Supabase when organizationId maps to non-certification org, (2) returns empty List when Supabase returns empty array for certification org, (3) returns mapped List for certification org with records, (4) throws RepositoryException when Supabase throws PostgrestException for certification org query, (5) both mentorId and organizationId non-empty validation. Verify Supabase is NOT called in the null-return branch.

Component
Certification Record Repository
data low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for peer mentor data may block coordinator queries if the RLS rules are written for peer-mentor-self access only, requiring policy updates that affect other features sharing the same tables.

Mitigation & Contingency

Mitigation: Review existing RLS policies on peer_mentors, certification_records, and activity_log tables before writing repository queries. Coordinate with the database team to add coordinator-role predicates without weakening existing mentor-self policies.

Contingency: If policy changes are blocked, implement a Supabase Edge Function as a secure query proxy that enforces authorization server-side, avoiding direct RLS policy modification.

medium impact medium prob technical

The activity log table schema may not have a mentor_id foreign key column or may require a JOIN through an intermediate table, making the aggregation query significantly more complex than anticipated.

Mitigation & Contingency

Mitigation: Inspect the actual Supabase activity_log table schema before starting the MentorActivityLogRepository implementation. Document the exact JOIN path needed and validate it returns correct results for a known mentor.

Contingency: If schema requires complex multi-table aggregation, implement a Supabase database function (RPC) and expose it via the repository's fetchSummary method to keep Dart code clean.

high impact low prob dependency

The Blindeforbundet assignment table may not yet exist in the shared Supabase schema or may have a different structure than assumed, blocking the AssignmentHistoryRepository implementation.

Mitigation & Contingency

Mitigation: Verify the assignments table exists and confirm its column structure with the Contact Detail & Edit Screen team which also depends on assignment data (assignment-repository in that feature).

Contingency: If the assignments table is not yet available, implement the AssignmentHistoryRepository with a stub returning empty list and a TODO marker, unblocking the aggregation service while the schema is finalized.