critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

Contact model class is immutable (all fields final) with fields: id (String), name (String), organizationId (String), role (ContactRole enum), notes (String?), chapterAffiliations (List<String>), assignmentStatus (AssignmentStatus enum), and any org-label-dependent display fields
PeerMentor model extends or implements Contact, adding peer-mentor-specific fields: availabilityStatus, certificationStatus (with expiry date), assignedMemberCount (int), and chapter (String)
ContactRole enum covers at minimum: coordinator, peerMentor, orgAdmin with a stable string representation for JSON round-tripping
AssignmentStatus enum covers: active, paused, inactive with string representation
fromJson factory on Contact safely handles null for every nullable field without throwing; missing required fields fall back to safe defaults with an assertion or logged warning rather than a runtime crash
fromJson factory on PeerMentor correctly identifies records by the role field value and populates peer-mentor-specific fields; unknown role values default to ContactRole.coordinator without exception
toJson() method serializes the model back to a Map<String, dynamic> compatible with Supabase insert/update payloads
All model classes implement == and hashCode based on id field
Models include a copyWith() method for immutable updates
Unit tests pass for: happy-path parse, all-null optional fields, unknown enum values, round-trip toJson → fromJson produces equal object

Technical Requirements

frameworks
Flutter
Dart
apis
Supabase REST (JSON response shape)
data models
Contact
PeerMentor
ContactRole
AssignmentStatus
performance requirements
fromJson must complete in under 1ms per record to support list rendering of 200+ contacts without jank
No heap allocations beyond the model object itself (avoid intermediate Maps in hot path)
security requirements
Model must not expose raw Supabase row data beyond defined fields — no dynamic Map passthrough
Sensitive fields (notes, chapter affiliations) must be marked for encryption consideration at the repository layer

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define ContactRole and AssignmentStatus as Dart enums with a static fromString(String) factory that uses a switch with a default case — never throw on unknown values from the server. Use the freezed or equatable package if already in pubspec for == and copyWith boilerplate; otherwise implement manually to keep dependencies minimal. Place models in lib/features/contacts/domain/models/. PeerMentor should be a separate class (not a subclass of Contact) to avoid Liskov substitution issues with the role field — share a common ContactBase interface instead.

The Supabase contacts table likely uses snake_case column names; ensure fromJson maps organization_id → organizationId etc. Document the exact Supabase column names as constants at the top of each model file.

Testing Requirements

Unit tests using flutter_test covering: (1) Contact.fromJson with complete valid JSON, (2) Contact.fromJson with all optional fields null, (3) Contact.fromJson with unknown role string defaults gracefully, (4) PeerMentor.fromJson correctly sets certificationStatus and assignedMemberCount, (5) toJson → fromJson round-trip equality, (6) copyWith produces new instance with expected field change. Target 100% branch coverage on fromJson parsing logic.

Component
Contact Repository
data medium
Epic Risks (2)
high impact medium prob security

Existing Supabase RLS policies for the contacts and peer_mentors tables may not align with the application-level UserRole model, causing ContactRLSQueryBuilder to construct filter expressions that are redundant, conflicting, or that allow over-fetching. In a multi-chapter context (NHF), this could expose contacts belonging to other chapters.

Mitigation & Contingency

Mitigation: Audit and document the existing RLS policies against the UserRole enum before writing a single line of query builder code. Write integration tests asserting cross-organization data isolation using separate test user tokens for each role.

Contingency: If RLS policies are misaligned at runtime, add an explicit application-level organization_id equality check in ContactRepository as a secondary guard while the database policies are corrected in a coordinated migration.

medium impact low prob integration

The Supabase schema for contacts and peer_mentors tables may differ from the expected typed models — missing columns, renamed fields, or type mismatches — causing deserialization failures that surface only at runtime during integration testing.

Mitigation & Contingency

Mitigation: Document expected schema fields upfront and validate against the live Supabase schema at sprint start. Use freezed and json_serializable for compile-time-safe deserialization with explicit required/optional field declarations.

Contingency: Introduce nullable fields with safe defaults for any schema mismatches discovered in testing; log deserialization errors to the monitoring service so schema drift is caught before production deployment.