high priority medium complexity backend pending backend specialist Tier 2

Acceptance Criteria

getActivitiesRecordedByCoordinator(coordinatorId, {dateRange, featureFilters}) returns only rows where recorded_by_user_id == coordinatorId
Result set includes peer_mentor_id and resolved peer mentor display name (first_name + last_name) via join on profiles/contacts table
Pagination is supported via cursor or offset; page size is configurable with a default of 20
dateRange filter (startDate, endDate inclusive) correctly narrows results; null dateRange returns all records
featureFilters (activity_type_id list) further narrows results when provided; empty list returns all types
A coordinator cannot retrieve rows recorded by a different coordinator — RLS enforces this at the Supabase level
Method returns an empty list (not an error) when no records match the filters
Response time is under 500 ms for up to 500 rows on a standard Supabase plan
Returned model includes: activity id, date, duration, activity_type name, peer_mentor_id, peer mentor full name, recorded_by_user_id
All Supabase query errors are mapped to typed domain failures (e.g., NetworkFailure, UnauthorizedFailure)

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15
Supabase Auth
data models
activity
contact
assignment
performance requirements
Query must use an index on (recorded_by_user_id, date) to avoid full table scans
Pagination prevents unbounded result sets; max page size capped at 100
Join for peer mentor name must not cause N+1 queries — use a single SELECT with JOIN
security requirements
RLS policy on activities table must restrict SELECT to rows where recorded_by_user_id matches auth.uid() for coordinator role
coordinatorId parameter must be validated against the authenticated session — never trust client-supplied ID directly
No PII beyond display name returned in the list view; full details fetched on demand

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use Supabase's `.select()` with a foreign-key join pattern: `activities.select('*, contacts(first_name, last_name)')` filtered by `recorded_by_user_id`. Do not perform a separate query for mentor names. Define a `ProxyAuditRecord` value object that combines the activity fields with the resolved mentor name. Apply `.range(offset, offset + pageSize - 1)` for pagination.

Wrap the Supabase call in a try/catch and map `PostgrestException` to the project's typed failure hierarchy. Keep this method read-only — no side effects. Coordinate with the RLS policy definition from task-002 to ensure the filter column is indexed.

Testing Requirements

Unit tests using flutter_test with a mocked Supabase client. Test scenarios: (1) returns only rows matching coordinatorId, (2) dateRange filter correctly excludes out-of-range rows, (3) featureFilters narrows by activity_type_id, (4) pagination returns correct page and cursor, (5) empty result returns empty list without error, (6) Supabase error maps to typed domain failure, (7) peer mentor name is present in each result item. Integration test: verify RLS prevents cross-coordinator data access using two seeded coordinator sessions.

Component
Proxy Activity Repository
data medium
Epic Risks (2)
high impact low prob security

The Proxy Registration Service must verify that the coordinator has a legitimate assignment relationship with the target peer mentor before creating a record. If this check is implemented only in application code and not enforced at the DB/RLS level, a compromised or buggy client could bypass it by calling the Supabase endpoint directly, creating fraudulent proxy records for arbitrary peer mentors.

Mitigation & Contingency

Mitigation: Implement permission validation at two levels: (1) application-layer check in Proxy Registration Service that queries the assignments table before constructing the payload, and (2) RLS policy on the activities table that restricts INSERT to rows where recorded_by_user_id matches the authenticated user AND peer_mentor_id is in the set of peer mentors assigned to that coordinator. The RLS policy is the authoritative guard; the service-layer check provides early user-facing feedback.

Contingency: If RLS policy implementation is blocked by Supabase plan constraints, implement a Supabase Edge Function as a proxy endpoint that enforces the permission check server-side before forwarding to the DB. Disable direct client inserts entirely for proxy activities.

medium impact medium prob technical

For a bulk session with 30 selected peer mentors, the Proxy Duplicate Detector must query existing activities for each mentor. If implemented as 30 sequential Supabase queries, round-trip latency could make the bulk confirmation screen feel slow (>3s), degrading coordinator experience and potentially causing timeouts.

Mitigation & Contingency

Mitigation: Implement the duplicate check as a single Supabase query using an IN clause on peer_mentor_id combined with the activity_type and date filters, returning all potential duplicates for the entire batch in one network round-trip. Group results client-side by mentor ID to produce the per-mentor warning structure.

Contingency: If the single-query approach returns too much data for very large chapters, add a database index on (peer_mentor_id, activity_type, date) and profile query time. If still insufficient, accept a short loading state on the confirmation screen with a progress indicator rather than pre-loading duplicates before navigation.