critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ContactRLSQueryBuilder class exists in the correct package path and is importable from the service layer without circular dependencies
A static or constant mapping exists that covers all three UserRole values: coordinator, org_admin, and peer_mentor
Each mapping entry resolves to a typed descriptor (e.g., a sealed class or record) that encodes the Supabase column name, operator, and value pattern — not a raw string — to allow type-safe construction
A public method signature (stub, not yet implemented) exists for each downstream query operation: fetchContacts, fetchPeerMentors, and applySearch
The class is annotated or documented such that the interface contract is clear to implementors of the downstream methods
Compilation succeeds with no warnings after this task (npm run build equivalent: flutter analyze returns no errors on this file)
No hardcoded Supabase table names or column names exist outside of a single constants file — all references go through the mapping table
The mapping covers the case where org_admin has a broader filter scope than coordinator, reflecting the NHF multi-chapter hierarchy

Technical Requirements

frameworks
Flutter
Dart
apis
Supabase (query builder interface)
data models
UserRole
Contact
PeerMentor
performance requirements
Mapping lookups must be O(1) — use a Dart Map constant, not a switch statement chain
security requirements
No role's filter expression must allow access to contacts outside its authorised scope — the mapping must be reviewed against the Supabase RLS policy definitions before merging
The mapping must not contain a wildcard/all-access entry that could be triggered by a null or unrecognised role value; an unknown role must throw an ArgumentError

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define the filter descriptor as a simple Dart class or record with fields: columnName (String), operator (enum: eq, in, contains), and value (dynamic). This avoids string concatenation errors later. Place the mapping in a private static const Map inside ContactRLSQueryBuilder — do not expose it publicly to prevent external mutation. For the org_admin role, the filter descriptor may need a 'scope' flag to indicate it queries across multiple organisation units; model this now even if the multi-org logic is implemented in a later task.

Follow Dart naming conventions: snake_case for file names, PascalCase for classes, camelCase for methods.

Testing Requirements

Unit tests (added in task-004) will cover this class, but during this task verify the mapping by writing a simple assertion script or a minimal dart test that iterates all UserRole values and confirms each resolves to a non-null, non-empty filter descriptor. This smoke test should run in under 1 second and guards against typos in enum values. Use flutter_test.

Component
Contact RLS Query Builder
infrastructure low
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.