Define ContactChapter domain model and mapper
epic-multi-chapter-membership-handling-foundation-task-003 — Create the ContactChapter Dart domain class with all required fields (contactId, chapterId, createdAt, etc.). Implement a bidirectional mapper that converts between ContactChapter domain objects and raw Supabase PostgREST JSON response maps. Ensure null-safety and proper type coercion for all fields.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Check whether the project uses `freezed` for data classes — if so, annotate `ContactChapter` with `@freezed` and generate via `build_runner` for `copyWith`, `==`, `hashCode`, and `toString` for free. If not, implement manually and keep it consistent with other domain models. For DateTime parsing, use `DateTime.parse(json['joined_at'] as String)` and wrap in a try/catch that throws a domain-level `MappingException` with context rather than a raw `FormatException`. Use `_` prefixed private constructor pattern if `freezed` is not in use, exposing a factory constructor `ContactChapter({...})` for clarity.
Snake_case to camelCase key mapping must be explicit in the mapper — do not rely on `json_serializable` auto-naming unless it is already the project standard.
Testing Requirements
Unit tests in `test/domain/models/contact_chapter_test.dart` and `test/data/mappers/contact_chapter_mapper_test.dart`. Cover: (1) `fromJson` with a complete valid JSON map returns a correctly populated `ContactChapter`; (2) `fromJson` with `role_in_chapter: null` returns a `ContactChapter` with `roleInChapter == null`; (3) `toInsertJson` does not include `created_at` or `updated_at` keys; (4) two `ContactChapter` instances with the same `contactId` and `chapterId` are equal (`==` returns true). Aim for 100% line coverage of both files.
Supabase RLS policies for a junction table that spans organisations are non-trivial. An incorrectly scoped policy could expose chapter affiliations from other organisations to coordinators, constituting a data breach.
Mitigation & Contingency
Mitigation: Draft RLS policies in a staging environment and run an explicit cross-organisation isolation test suite before merging. Use Supabase policy testing tools and peer review all policy definitions.
Contingency: If a policy error reaches review, roll back the migration and apply a corrective patch. Ensure no production data has been exposed by auditing Supabase logs for cross-organisation query results.
The contact_chapters table migration may conflict with existing foreign key structures or require a backfill for contacts already assigned to a single chapter, causing migration failures in production.
Mitigation & Contingency
Mitigation: Write the migration as an additive, non-destructive operation. Backfill existing single-chapter assignments by deriving them from the existing contact records. Test the full migration on a production-sized dataset clone before release.
Contingency: Provide a rollback migration script that removes the new table without touching existing contact records. Coordinate with operations for a maintenance window if a re-run is needed.