critical priority medium complexity backend pending backend specialist Tier 0

Acceptance Criteria

PeerMentorDetailModel is an immutable Dart class (`@immutable`) with a `const` constructor where possible
Model contains a required `profile` field typed to an existing peer mentor profile model (name, contact details, org membership, pause status)
Model contains a required `certifications` field typed as `List<PeerMentorCertification>` (may be empty list, never null)
Model contains a required `recentActivities` field typed as `List<Activity>` (may be empty list, never null)
Model contains a required `assignments` field typed as `List<Assignment>` (may be empty list, never null)
Model contains nullable org-specific fields clearly documented with `// org-specific: [org name]` inline comments
Model contains a per-source error map: `Map<PeerMentorDataSource, String?> sourceErrors` where `PeerMentorDataSource` is an enum of the four repository sources
Model exposes a computed getter `bool hasPartialData` returning true when any source error is non-null
Model implements `==`, `hashCode`, and `copyWith` for all fields
Model does not contain any async methods, BLoC events, or UI dependencies
All field types are explicitly typed — no `dynamic` fields
Model file includes a `factory PeerMentorDetailModel.empty()` constructor returning a safe default (empty lists, null errors) for initial BLoC state
Existing entity field names match the database schema column names defined in the data model context (id, peer_mentor_id, organization_id, etc.)

Technical Requirements

frameworks
Flutter
Dart
BLoC
apis
Supabase PostgreSQL 15 (via repository pattern)
data models
certification
activity
assignment
contact
performance requirements
Model construction is O(n) where n is the sum of list lengths — no sorting or heavy computation in the constructor
copyWith must not deep-clone lists unnecessarily — pass through the same list reference unless explicitly replaced
security requirements
Model must not expose raw Supabase row objects or contain database connection references
PII fields (email, phone) are present only on the profile sub-model — not duplicated at the top level
sourceErrors map must not contain raw exception stack traces — only sanitised user-facing or developer-facing message strings

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place the model in the peer mentor feature domain layer (e.g., `lib/features/peer_mentor/domain/models/peer_mentor_detail_model.dart`), not in a shared folder, since it is specific to this aggregation use case. Define `PeerMentorDataSource` as a Dart enum in the same file or an adjacent `enums.dart`. For `copyWith`, consider using the `copyWith` code-gen pattern already used elsewhere in the project for consistency. The four repository sources for the enum should be: `profile`, `certifications`, `activities`, `assignments`.

Document the nullable org-specific fields with a reference to the organisation that requires them (e.g., HLF certification expiry, Blindeforbundet assignment encryption status) so future developers understand the conditional logic. This model is the single source of truth consumed by the BLoC — downstream BLoC states should wrap or extend this model rather than duplicating fields.

Testing Requirements

Unit tests only (no widget pump). Test `factory PeerMentorDetailModel.empty()` produces a valid instance with empty lists and no errors. Test `hasPartialData` returns false when all sourceErrors are null, and true when any single source error is set. Test `copyWith` correctly replaces individual fields while preserving others.

Test `==` equality for two identically constructed instances. Test that passing a null list argument to a list field throws an assertion error (if assertions are enabled) — list fields must never be null. No Supabase mocking needed at this layer since the model is a pure data class.

Component
Peer Mentor Profile Aggregation Service
service medium
Epic Risks (3)
medium impact medium prob technical

The parallel Future.wait aggregation pattern may produce race conditions or incorrect merged state when some repositories resolve significantly faster than others, particularly if the BLoC receives a RefreshDetail event while a prior fetch is still in flight.

Mitigation & Contingency

Mitigation: Implement cancellation token pattern in the aggregation service to abort in-flight fetches on new events. Add BLoC test scenarios for rapid successive refresh events to validate state consistency.

Contingency: If race conditions persist, switch to a sequential-with-timeout fetch strategy for the first release and profile the performance impact before deciding whether parallel fetch optimization is worth the complexity.

medium impact medium prob integration

Integrating PeerMentorDetailScreenWidget into the existing StatefulShellRoute navigation structure may conflict with the Contacts tab's existing route hierarchy, requiring changes to navigation-route-config that could affect other teams' features.

Mitigation & Contingency

Mitigation: Coordinate with the Contact List and Contact Detail feature teams before adding the new route. Review the existing StatefulShellRoute configuration and confirm the peer mentor detail route can be nested under the Contacts branch without path conflicts.

Contingency: If route conflicts arise, temporarily implement the peer mentor detail as a modal overlay (push route) rather than a shell route child, preserving functionality while the navigation architecture conflict is resolved.

low impact high prob dependency

The course enrollment screen that the certification alert banner links to may not yet exist or may be implemented in a separate feature epic, leaving a broken navigation tap for HLF users in the initial release.

Mitigation & Contingency

Mitigation: Check the certification management feature implementation status before finalizing Epic 4 scope. If the enrollment screen is not available, design the tap action to open the HLF course enrollment URL in an external browser as an interim solution.

Contingency: Implement the CTA as a configurable action: if the enrollment route exists in the router, push it; otherwise, launch the configured org-specific enrollment URL via url_launcher, ensuring HLF users can always take action on expired certifications.