high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

DuplicateConflict is a final class extending Equatable
ConflictSeverity is a Dart enum with values: warning, blocking
All fields final: conflictingActivityId (String), attributedTo (String), activityDate (DateTime), activityTypeId (String), severity (ConflictSeverity), resolvedAt (DateTime?)
fromJson parses activityDate and resolvedAt from ISO 8601 strings; resolvedAt accepts null
toJson omits resolvedAt key entirely when null (do not emit 'resolved_at': null unless Supabase requires it)
fromJson falls back to ConflictSeverity.warning for unrecognised severity strings
isResolved getter returns true when resolvedAt is not null
copyWith correctly handles nullable resolvedAt — must support explicitly setting it to null
Equatable props covers all six fields
Unit test: round-trip for warning and blocking severity
Unit test: resolvedAt null round-trip and non-null round-trip
Unit test: isResolved getter correctness
File placed at lib/features/proxy_activity/models/duplicate_conflict.dart

Technical Requirements

frameworks
Flutter
equatable
apis
Supabase RPC (duplicate detection RPC response shape)
data models
DuplicateConflict
activities (conflicting row)
performance requirements
Model is immutable and allocation is cheap — no performance concerns at this layer
security requirements
conflictingActivityId and attributedTo are UUIDs — treat as opaque identifiers, never display raw in user-facing error messages
resolvedAt timestamp should be server-generated, not client-supplied, to prevent tampering
ui components
ConflictBanner (inline warning/blocking indicator in bulk registration UI)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

The nullable copyWith problem in Dart: standard copyWith cannot distinguish between 'not provided' and 'explicitly null'. Use the sentinel/wrapper pattern: define a private `_Sentinel` class and use `Object? resolvedAt = _sentinel` in copyWith, then check `identical(resolvedAt, _sentinel)` to decide whether to use the existing value or the new one. This is the idiomatic Dart approach when freezed is not used.

The isResolved getter is a convenience for UI conditional rendering — add it directly to the class. ConflictSeverity should be defined in the same file or a shared enums file. Since this model is consumed by upstream duplicate detection services, ensure the JSON field names exactly match what those services produce (coordinate with the RPC response shape defined in task-002).

Testing Requirements

Write flutter_test unit tests in test/features/proxy_activity/models/duplicate_conflict_test.dart. Cover: (1) full round-trip with resolvedAt populated, (2) round-trip with resolvedAt=null, (3) isResolved returns false when resolvedAt is null, (4) isResolved returns true when resolvedAt is set, (5) copyWith(resolvedAt: someDateTime) creates resolved instance, (6) severity fallback for unknown string, (7) Equatable equality. Minimum 7 test cases. Use a nullable copyWith pattern that supports `copyWith({DateTime?

resolvedAt, bool clearResolvedAt = false})` or a wrapper object pattern to allow explicitly nulling the field.

Component
Proxy Activity Data Models
data low
Epic Risks (3)
high impact medium prob technical

The activities table migration adding registered_by and attributed_to columns may conflict with existing RLS policies or FK constraints if the user profile table structure differs from assumptions, blocking all subsequent epics.

Mitigation & Contingency

Mitigation: Review existing activities table schema and RLS policies before writing the migration. Run the migration against a staging database clone first. Write rollback scripts alongside the migration.

Contingency: If migration fails in staging, isolate the conflict with a targeted schema audit, adjust FK references or RLS policy scope, and re-run before touching production.

high impact medium prob security

The RLS policy must filter proxy inserts to the coordinator's chapter scope. If the chapter-scope resolver pattern differs between organisations (multi-chapter coordinators in NHF vs single-chapter in HLF), the policy may be too broad or too restrictive.

Mitigation & Contingency

Mitigation: Design the RLS policy to accept a coordinator's full set of assigned chapter IDs (array) rather than a single chapter_id. Validate the policy against NHF multi-chapter test fixtures during the integration test phase.

Contingency: If the policy is found to be incorrect after deployment, introduce a server-side validation edge function as a safety net while the RLS policy is corrected.

medium impact low prob technical

The bulk_register_activities RPC function may time out or cause lock contention when inserting large participant batches (e.g. 40+ peer mentors in a single group session), degrading the user experience.

Mitigation & Contingency

Mitigation: Benchmark the RPC function with 50-participant batches during development. Use unnest-based bulk insert rather than row-by-row PL/pgSQL loops. Set a reasonable statement_timeout.

Contingency: If performance is insufficient, split the client-side submission into chunks of 20 participants with progress feedback, rather than a single RPC call.