high priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

DuplicateWarningEvent is an immutable Dart class with fields: eventId (String, UUID), timestamp (DateTime), contactId (String), involvedChapterIds (List<String>), suspectedDuplicateActivityId (String), activityDate (DateTime), activityType (String), coordinatorDecision (CoordinatorDecision enum: dismissed | cancelled)
CoordinatorDecision is a Dart enum with values dismissed and cancelled, and a toJson() helper returning the string representation
DuplicateWarningEvent implements toJson() returning a Map<String, dynamic> with ISO 8601 strings for DateTime fields and a List<String> for involvedChapterIds
DuplicateWarningEvent implements a factory fromJson(Map<String, dynamic>) constructor for round-trip deserialization
Round-trip test: toJson() then fromJson() produces an equal object
Abstract class DuplicateWarningEventLogger has a single method: Future<void> logWarningEvent(DuplicateWarningEvent event)
Both files reside in the domain layer with no data-layer imports
dart analyze reports zero errors or warnings on both files
eventId defaults to a newly generated UUID v4 if not supplied, using the uuid package

Technical Requirements

frameworks
Flutter
Dart
data models
duplicate_warning_events
activities
contacts
chapters
performance requirements
toJson() must complete synchronously — no async operations
The model must be safe to instantiate on the UI thread (no heavy computation)
security requirements
DuplicateWarningEvent must not include personal contact information beyond contactId — no names, addresses, or health data
eventId must be a UUID v4 (random) to prevent enumeration of audit records

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use the uuid package (already likely in pubspec.yaml for other models) to generate eventId. Define the model in lib/domain/models/duplicate_warning_event.dart and the enum in the same file or a sibling file. Consider using freezed for the model class to avoid boilerplate — if freezed is already used in the project, follow that convention. For the toJson/fromJson, if using freezed with json_serializable, annotate with @JsonSerializable().

The CoordinatorDecision enum should use @JsonValue('dismissed') and @JsonValue('cancelled') annotations if json_serializable is used, to ensure the exact string values match what the Supabase column expects. The DuplicateWarningEventLogger interface file lives in lib/domain/repositories/duplicate_warning_event_logger.dart — naming it as a 'logger' rather than 'repository' signals its audit-only, write-once semantics.

Testing Requirements

Write unit tests in flutter_test: (1) construct a DuplicateWarningEvent with known values, call toJson(), and assert each field is correctly serialized (DateTime as ISO 8601 string, enum as string, list as JSON array); (2) call fromJson() on the serialized map and assert the reconstructed object equals the original; (3) verify that omitting eventId in the constructor generates a non-null UUID string; (4) verify CoordinatorDecision.dismissed.toJson() returns 'dismissed' and CoordinatorDecision.cancelled.toJson() returns 'cancelled'; (5) verify fromJson() with coordinatorDecision: 'dismissed' reconstructs CoordinatorDecision.dismissed. No integration tests required — this is a pure domain model.

Component
Duplicate Warning Event Logger
infrastructure low
Epic Risks (3)
high impact medium prob technical

The Cross-Chapter Activity Query must avoid N+1 fetches across chapters. If naively implemented as a per-chapter loop, it will cause severe performance degradation for contacts affiliated with 5 chapters on poor mobile connections.

Mitigation & Contingency

Mitigation: Design the query as a single PostgREST join of contact_chapters and activities on contact_id from the start. Add a query performance test with 5 affiliations and 100+ activities to the integration test suite and enforce a maximum execution time threshold.

Contingency: If a performance regression is detected post-merge, introduce a Supabase RPC function (stored procedure) to move the join server-side, bypassing any client-side N+1 pattern.

high impact low prob security

If the Duplicate Warning Event Logger write fails silently (network error, RLS denial), audit entries will be missing from the Bufdir compliance record without the user being aware.

Mitigation & Contingency

Mitigation: Implement the logger with a local fallback queue: if the Supabase write fails, persist the event locally and retry on next launch. Log all failures to a verbose output channel.

Contingency: Add a reconciliation job that compares locally queued events to Supabase entries and re-submits any gaps. Provide a data export of the local queue for manual audit if reconciliation fails.

medium impact low prob technical

Two coordinators simultaneously adding the 5th chapter affiliation for the same contact could bypass the maximum enforcement check if both reads occur before either write completes.

Mitigation & Contingency

Mitigation: Enforce the 5-affiliation maximum as a database-level constraint (CHECK + trigger or RPC with a FOR UPDATE lock) rather than relying solely on application-layer validation.

Contingency: If a constraint violation is detected in production, run a corrective query to end the most recently created excess affiliation and notify the relevant coordinator.