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.