Build BufdirSchemaConfigRepository Dart class
epic-bufdir-reporting-export-foundation-task-008 — Implement the BufdirSchemaConfigRepository class in Dart. Expose methods: getActiveSchema(String orgId), getAllVersions(String orgId), and upsertSchemaVersion(BufdirColumnSchema). Implement local caching via Riverpod provider to avoid redundant fetches. Include version comparison logic to detect schema drift between exports.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Implement the Riverpod cache as an AsyncNotifier
Keep this function in a separate schemaDriftUtils.dart file for testability. The upsert conflict check (same version, different columns) should be performed by fetching the existing row before upsert and comparing — do not rely on a database constraint alone since Supabase upsert silently overwrites by default.
Testing Requirements
Unit tests with mocktail: (1) getActiveSchema returns null when Supabase returns empty list; (2) getActiveSchema returns the highest-version row when multiple rows exist; (3) upsertSchemaVersion throws SchemaUpsertForbiddenException when JWT role is not super_admin; (4) upsertSchemaVersion calls Supabase upsert with correct payload; (5) detectSchemaDrift returns empty SchemaDriftResult for identical schemas; (6) detectSchemaDrift correctly identifies one added column; (7) detectSchemaDrift correctly identifies one removed column; (8) cache returns cached value on second call without triggering Supabase fetch; (9) cache invalidates after upsertSchemaVersion. Integration tests: verify that getActiveSchema fetches the live active schema and that upsertSchemaVersion succeeds for a super_admin token. Minimum 85% line coverage on the repository and drift detection logic.
RLS policies for the audit log and schema config tables must correctly handle multi-chapter membership hierarchies (up to 1,400 local chapters for NHF). Incorrect policies could either over-expose data across organisations or prevent legitimate coordinator access, both of which are serious compliance failures.
Mitigation & Contingency
Mitigation: Design RLS policies using the existing org hierarchy resolver pattern. Write integration tests that verify cross-organisation isolation with representative fixture data covering NHF's multi-level hierarchy before merging.
Contingency: If RLS policies prove too complex to express safely in Postgres, implement a Supabase Edge Function as a data access proxy that enforces isolation in application code, with RLS serving as a secondary defence layer.
Bufdir's column schema is expected to evolve as Norse Digital Products negotiates a simplified digital reporting format. If the schema config versioning model is too rigid, applying Bufdir schema updates without a code deployment could fail, forcing emergency releases.
Mitigation & Contingency
Mitigation: Design the schema config table to store the full JSON column mapping as a JSONB field with a version number. Provide an admin API to upsert new versions without any schema migration required.
Contingency: If the versioning model is insufficient for a Bufdir schema change, fall back to a code deployment with the updated default schema, using the database config only for org-specific overrides.