critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

ProxyActivityRecord is an immutable Dart class (all fields final) with fields: id (String UUID), actingCoordinatorId (String), attributedMentorId (String), activityType (String), activityDate (DateTime), durationMinutes (int), notes (String?), status (ProxyActivityStatus), createdAt (DateTime), updatedAt (DateTime)
ProxyActivityRecord includes a copyWith method, equality based on id, and a toJson/fromJson pair compatible with the Supabase proxy_activities table column names (snake_case)
ProxyActivityStatus is a Dart enum with values: pending, submitted, approved, rejected, duplicate; each value has a displayLabel getter returning a human-readable English string
ProxyActivityRequest is an immutable value object with fields: attributedMentorId, activityType, activityDate, durationMinutes, notes (optional), recurringTemplateId (optional); it has no id or status field (assigned by the service layer)
ProxyActivityResult is a sealed class with two subtypes: ProxyActivityResult.success(ProxyActivityRecord record) and ProxyActivityResult.failure(ProxyRegistrationError error)
ProxyRegistrationError is a sealed class with subtypes: ValidationError(ValidationFailureReason reason), DuplicateDetected(String existingRecordId), RepositoryError(String message), AuditError(String message)
ValidationFailureReason is an enum: selfAttribution, mentorOutOfScope, mentorInactive, missingActivityType, invalidDate
All models live in a dedicated lib/domain/proxy_activity/ directory and have no Flutter framework dependencies (pure Dart)
A ProxyActivityRepository abstract interface (abstract class) is defined in the same directory with methods: Future<ProxyActivityRecord> save(ProxyActivityRequest request, String actingCoordinatorId) and Future<List<ProxyActivityRecord>> findByMentorAndDateRange(String mentorId, DateTime from, DateTime to)
Unit tests verify: ProxyActivityRecord toJson/fromJson round-trip, sealed class exhaustive pattern matching compiles without warnings, ProxyActivityStatus display labels are non-empty for all values

Technical Requirements

frameworks
Dart (pure, no Flutter dependency)
flutter_test (for unit tests only)
apis
Supabase table schema alignment (proxy_activities column names must match toJson keys)
data models
ProxyActivityRecord
ProxyActivityRequest
ProxyActivityResult
ProxyRegistrationError
ProxyActivityStatus
ValidationFailureReason
ProxyActivityRepository (abstract interface)
performance requirements
All models use const constructors where possible to enable compile-time constant evaluation
toJson/fromJson must not use reflection (no mirrors); use hand-written or code-generated (json_serializable) implementations
security requirements
actingCoordinatorId must not be a field on ProxyActivityRequest — it is always sourced from the authenticated session at the service layer to prevent spoofing
ProxyActivityRecord.fromJson must validate that required fields are non-null; throw a FormatException with a descriptive message if a required field is absent or of wrong type

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Dart 3 sealed classes for ProxyActivityResult and ProxyRegistrationError to get compile-time exhaustiveness checking. Consider using the freezed package if it is already a project dependency, as it provides copyWith, equality, and serialisation boilerplate for free; if freezed is not in use, implement manually. Place the abstract ProxyActivityRepository interface in the domain layer (not in the data layer) to maintain dependency inversion — the Supabase implementation will live in lib/data/repositories/ and depend on this interface. Use DateTime.toUtc() consistently in toJson to avoid timezone ambiguity when persisting to Supabase.

Document the dual attribution pattern in a brief code comment: actingCoordinatorId is who submitted the record (for accountability), attributedMentorId is whose activity it represents (for reporting).

Testing Requirements

Write unit tests using flutter_test covering: ProxyActivityRecord.toJson produces correct snake_case keys matching Supabase column names; ProxyActivityRecord.fromJson correctly deserialises a sample Supabase row map; copyWith produces a new instance with only the specified fields changed; ProxyActivityStatus.displayLabel returns non-empty string for all enum values; sealed class switch statements on ProxyActivityResult and ProxyRegistrationError compile without default case (exhaustiveness check). No mocking required — these are pure data model tests. All tests should run in under 1 second total.

Component
Proxy Registration Service
service medium
Epic Risks (3)
medium impact medium prob technical

The 2-hour window duplicate detection logic requires querying existing proxy records with compound key matching (mentor + date + activity type within time range). If the query is too broad it produces false positives that frustrate coordinators; if too narrow it misses genuine duplicates that corrupt Bufdir data.

Mitigation & Contingency

Mitigation: Define the duplicate detection window as a configurable parameter from the start. Prototype the Supabase query with representative data covering edge cases (midnight boundaries, different activity types same day, same activity type different mentors) before finalising the implementation.

Contingency: If the detection produces excessive false positives in UAT, allow coordinators to explicitly acknowledge and bypass the duplicate warning with a reason field, preserving safety while reducing friction.

high impact medium prob scope

If the proxy registration form does not clearly distinguish between the acting coordinator and the attributed mentor, coordinators may submit records attributing activities to themselves, causing inaccurate Bufdir reporting and potential funding issues.

Mitigation & Contingency

Mitigation: Conduct UAT with at least one real coordinator via TestFlight before release. Use distinct visual treatment (different card colours, explicit 'Registering on behalf of:' label) and require the confirmation screen to show both identities prominently.

Contingency: Add a mandatory confirmation checkbox on the confirmation screen that explicitly names the attributed mentor, preventing accidental self-attribution from slipping through.

high impact medium prob security

Coordinators with multi-chapter access must select an active chapter context before the mentor list is filtered correctly. If chapter scope resolution fails or is bypassed, cross-org proxy registrations could occur, violating data isolation between chapters.

Mitigation & Contingency

Mitigation: Reuse the existing active-chapter-state and hierarchy-service components established by the org hierarchy feature. Add a guard that blocks entry to the proxy flow if no chapter context is active, prompting chapter selection first.

Contingency: If the chapter resolution service is unavailable, default to the most restrictive scope (no mentors visible) and surface a clear error message rather than showing an unfiltered mentor list.