Schema-only commit (1 of 8) for the 4-Augen-Prüfung workflow per docs/design-approvals-2026-05-06.md. No Go code reads these yet — paliad behaves identically until commit 2 wires ApprovalService into the mutation paths. Migration 054 adds: 1. `senior_pa` to paliad.project_teams.role CHECK. Drops both the English `project_teams_role_check` and the German-legacy `projekt_teams_role_check` (live-DB constraint name carried over from migration 018's pre-rename era). 2. `paliad.approval_role_level(text) RETURNS int IMMUTABLE` — strict ladder helper: lead(5) > of_counsel(4) > associate(3) > senior_pa(2) > pa(1) > [local_counsel/expert/observer = 0 = ineligible]. Mirrors the upcoming Go `levelOf()`. 3. `paliad.approval_policies` (project_id, entity_type, lifecycle_event, required_role) — UNIQUE composite key gives at most 8 rows per project. RLS: SELECT via can_see_project; INSERT/UPDATE/DELETE only for global_admin (defence-in-depth; service-role pool bypasses RLS, so the actual gate is service-layer). 4. `paliad.approval_requests` — operational pending workflow. pre_image jsonb captures revert state; payload echoes the diff; required_role snapshots the policy at request time. CHECK `decided_by != requested_by` is the second layer of self-approval block. RLS = same can_see_project predicate as deadlines / appointments — anyone with project visibility sees pending requests. 5. `approval_status` (default 'approved'), `pending_request_id`, `approved_by`, `approved_at` columns on both deadlines and appointments. `appointments.completed_at` (new) lands the appointment:complete lifecycle event. 6. Backfill: every existing deadline + appointment row marked approval_status='legacy'. Per Q11, no retroactive approval; the next mutation on a legacy row that hits an active policy follows the normal flow. Live-DB dry-run verified end-to-end: 20 deadlines + 5 appointments backfill, both new tables instantiate cleanly, helper function returns correct levels, self-approval CHECK fires on invalid INSERT, valid pending insert succeeds.
43 lines
1.7 KiB
SQL
43 lines
1.7 KiB
SQL
-- t-paliad-138: rollback dual-control approvals.
|
|
--
|
|
-- Reverses 054_approvals.up.sql:
|
|
-- 1. Drop appointment + deadline approval columns.
|
|
-- 2. Drop paliad.approval_requests.
|
|
-- 3. Drop paliad.approval_policies.
|
|
-- 4. Drop paliad.approval_role_level().
|
|
-- 5. Restore project_teams.role CHECK without 'senior_pa'.
|
|
--
|
|
-- Step 5 will fail loudly if any user has been re-roled to 'senior_pa' —
|
|
-- intentional, mirrors the t-paliad-051 down strategy. Operator must
|
|
-- migrate those rows to another role before rolling back.
|
|
|
|
ALTER TABLE paliad.appointments
|
|
DROP COLUMN IF EXISTS completed_at,
|
|
DROP COLUMN IF EXISTS approved_at,
|
|
DROP COLUMN IF EXISTS approved_by,
|
|
DROP COLUMN IF EXISTS pending_request_id,
|
|
DROP COLUMN IF EXISTS approval_status;
|
|
|
|
ALTER TABLE paliad.deadlines
|
|
DROP COLUMN IF EXISTS approved_at,
|
|
DROP COLUMN IF EXISTS approved_by,
|
|
DROP COLUMN IF EXISTS pending_request_id,
|
|
DROP COLUMN IF EXISTS approval_status;
|
|
|
|
DROP INDEX IF EXISTS paliad.deadlines_approval_status_pending_idx;
|
|
DROP INDEX IF EXISTS paliad.appointments_approval_status_pending_idx;
|
|
|
|
DROP TABLE IF EXISTS paliad.approval_requests;
|
|
DROP TABLE IF EXISTS paliad.approval_policies;
|
|
|
|
DROP FUNCTION IF EXISTS paliad.approval_role_level(text);
|
|
|
|
-- Drop by both English and the German-legacy name (see up migration §1).
|
|
ALTER TABLE paliad.project_teams DROP CONSTRAINT IF EXISTS projekt_teams_role_check;
|
|
ALTER TABLE paliad.project_teams DROP CONSTRAINT IF EXISTS project_teams_role_check;
|
|
ALTER TABLE paliad.project_teams ADD CONSTRAINT project_teams_role_check
|
|
CHECK (role IN (
|
|
'lead', 'associate', 'pa', 'of_counsel',
|
|
'local_counsel', 'expert', 'observer'
|
|
));
|