critical priority medium complexity database pending database specialist Tier 1

Acceptance Criteria

Table confidentiality_declarations exists in Supabase with all required columns: id (uuid PK), org_id (uuid FK), driver_id (uuid FK), template_version_id (uuid FK → declaration_templates), declaration_content (text, encrypted at rest), status (enum: pending/acknowledged/expired), sent_at (timestamptz), acknowledged_at (timestamptz nullable), created_at (timestamptz), updated_at (timestamptz), deleted_at (timestamptz nullable for soft-delete), deleted_by (uuid nullable)
RLS policies enforce org-scoped access: coordinators can INSERT and SELECT only within their own org_id, drivers can SELECT and UPDATE (acknowledge) only their own declarations, admins can SELECT all within their org
RLS policies prevent hard DELETE on any row — soft-delete via deleted_at is the only removal mechanism
Foreign key constraint to declaration_templates table is enforced with ON DELETE RESTRICT to prevent orphaned declarations
status column uses a database CHECK constraint or Postgres enum type allowing only 'pending', 'acknowledged', 'expired'
declaration_content is stored encrypted; Supabase Vault or pgcrypto encryption applied at column level
Migration script is idempotent and can be re-run without error
All timestamp columns default to now() where appropriate
Index exists on (org_id, driver_id) for efficient per-driver lookups
Index exists on (status, sent_at) for expiry processing queries

Technical Requirements

frameworks
Supabase Migrations (SQL)
pgcrypto or Supabase Vault for encryption
apis
Supabase Management API
Supabase Auth (for RLS policy references to auth.uid())
data models
confidentiality_declarations
declaration_templates
drivers
organizations
performance requirements
Index on (org_id, driver_id) to ensure per-driver queries complete in <50ms for orgs with up to 10,000 declarations
Index on status for batch expiry jobs processing up to 1,000 rows at a time
security requirements
declaration_content must be encrypted at rest using pgcrypto symmetric encryption or Supabase Vault secrets
RLS must be enabled on the table and all policies must be row-level org-scoped
No policy may allow cross-org data access under any authenticated role
Soft-delete enforced at RLS level: DELETE statements must be blocked entirely for all roles including service_role (use trigger or policy)
audit_log foreign key cascade must be handled — deleting a declaration must not be possible if audit rows reference it

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use a Supabase SQL migration file named with a timestamp prefix (e.g., 20260329_create_confidentiality_declarations.sql). Define the status column as a Postgres enum type (CREATE TYPE declaration_status AS ENUM ('pending','acknowledged','expired')) rather than a plain varchar with a CHECK constraint — this provides stronger type guarantees and is easier to extend. For encryption, prefer Supabase Vault (vault.create_secret / vault.decrypted_secrets) over raw pgcrypto if the Supabase project tier supports it, as Vault handles key rotation. If Vault is unavailable, use pgp_sym_encrypt/pgp_sym_decrypt with an app-level key stored in Supabase secrets.

Soft-delete enforcement: add a BEFORE DELETE trigger that raises an exception ('hard delete not permitted on confidentiality_declarations') — this is more reliable than an RLS policy alone because it also blocks service_role. The RLS policies should reference auth.uid() for the driver check and a helper function get_user_org_id() that reads from the user_profiles table for the org check.

Testing Requirements

Write SQL-level integration tests using Supabase's pgTAP or a migration test harness: (1) verify all columns exist with correct types and constraints; (2) verify RLS blocks cross-org SELECT for coordinator role; (3) verify driver can only UPDATE their own declaration status; (4) verify DELETE is blocked and returns an error; (5) verify soft-delete via deleted_at=now() succeeds; (6) verify FK to declaration_templates prevents orphaned rows; (7) verify status CHECK constraint rejects values outside the allowed enum. Also run Dart integration tests against the local Supabase emulator confirming that the DeclarationRepository (task-006) can insert, select, and soft-delete rows as expected.

Epic Risks (3)
high impact medium prob security

Row-level security policies for driver assignments and declarations must correctly scope data to the coordinator's chapter without leaking records across organizations. An incorrect RLS predicate could silently return empty result sets or, worse, expose cross-org data, both of which are difficult to detect in unit tests.

Mitigation & Contingency

Mitigation: Write dedicated RLS integration test scenarios with multiple org fixtures asserting both data isolation and correct data visibility. Use Supabase's built-in policy testing utilities and review policies with a second developer.

Contingency: If RLS policies prove too complex to get right quickly, implement application-layer org scoping as a temporary guard while RLS is fixed in a follow-up, with an explicit security review gate before production deployment.

high impact medium prob security

The declaration audit logger must produce tamper-evident records. If the database allows updates or deletes on audit rows, the compliance guarantee is broken. Supabase does not natively prevent row deletion by default.

Mitigation & Contingency

Mitigation: Implement an insert-only RLS policy on the audit table that denies UPDATE and DELETE for all roles including the service role. Add a database trigger that rejects mutation attempts and logs the attempt itself.

Contingency: If immutability cannot be enforced at the database level within the sprint, store audit entries in an append-only Supabase Edge Function log stream as a temporary alternative, with a migration plan to the proper table once constraints are implemented.

medium impact low prob technical

The org-feature-flag-service caches flag values to avoid repeated database reads. If the cache is not invalidated promptly after an admin toggles the flag, coordinators may see stale UI state — either seeing driver features when they should not, or not seeing them when they should.

Mitigation & Contingency

Mitigation: Use a Supabase Realtime subscription to listen for changes on the driver_feature_flag_config table and invalidate the in-memory cache immediately on change. Set a short TTL (60 seconds) as a safety net.

Contingency: If Realtime subscription proves unreliable, expose a manual cache-bust endpoint accessible from the admin toggle action, ensuring the cache is cleared synchronously on every flag change.