critical priority low complexity backend pending backend specialist Tier 3

Acceptance Criteria

buildProxyActivityRecord(mentorId, activityTypeId, date, duration, {notes}) assembles a ProxyActivityRecord with recorded_by_user_id from auth session
recorded_by_user_id is never accepted as a parameter — it is always read from the current Supabase auth session
peer_mentor_id is set to the confirmed mentorId parameter — the one validated by the permission check in task-007
Required fields (mentorId, activityTypeId, date, duration) are validated before assembly; missing required fields return ValidationFailure
date must not be in the future; a future date returns a ValidationFailure with a descriptive message
duration must be a positive integer (minutes); zero or negative duration returns ValidationFailure
notes field is optional; null or empty string is accepted and stored as null
The assembled ProxyActivityRecord is immutable — no field can be mutated after construction
If the auth session is unavailable (user logged out mid-flow), the method returns SessionExpiredFailure
The method is pure (no side effects, no DB calls) — all validation is local and synchronous except for the session read

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase Auth
data models
activity
activity_type
performance requirements
Method completes synchronously (or with a single async session read) in under 50 ms
No network calls — this is a local payload construction step
security requirements
recorded_by_user_id MUST be sourced from Supabase auth session only — never from any UI-supplied or external parameter
Validate that peer_mentor_id is not equal to recorded_by_user_id as an additional guard (coordinators registering for themselves should use the standard flow)
Notes field must be sanitized to remove leading/trailing whitespace; no HTML/script content allowed

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement as a synchronous factory method `ProxyActivityRecord.fromCoordinatorSession({required String mentorId, required String activityTypeId, required DateTime date, required int durationMinutes, String? notes, required String sessionUserId})` where `sessionUserId` is injected by the service (read from `supabase.auth.currentUser?.id`). This makes the record class independently testable without needing a Supabase instance. Validation rules should be expressed as a `validate()` method returning `Either` before the record is considered ready for the repository.

Keep the record a simple `@immutable` Dart class — no BLoC or Riverpod inside the model layer. Notes sanitization: `notes?.trim().isEmpty == true ? null : notes?.trim()`.

Testing Requirements

Unit tests with flutter_test: (1) valid inputs produce a correctly populated ProxyActivityRecord, (2) recorded_by_user_id matches mock session user ID not any input parameter, (3) missing activityTypeId returns ValidationFailure, (4) future date returns ValidationFailure, (5) zero duration returns ValidationFailure, (6) null notes produces null in record (not empty string), (7) session unavailable returns SessionExpiredFailure, (8) peer_mentor_id equals recorded_by_user_id returns ValidationFailure, (9) notes whitespace is trimmed before storage, (10) assembled record is equal-by-value to an expected fixture.

Component
Proxy Registration Service
service 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.