Files
paliad/internal/db/migrations/083_backfill_priority.up.sql
mAi b966d7c8cd feat(t-paliad-183): mig 083 — backfill priority per design §2.3
Phase 3 Slice 2 Step B-2. UPDATE paliad.deadline_rules.priority
from the legacy (is_mandatory, is_optional) pair per DESIGN §2.3
(NOT msg 1746's inverted mapping — head clarified in msg 1750
that design §2.3 is the load-bearing spec).

Mapping:
  T/F (153 rows) → 'mandatory'   (statutory must, ☑ pre-checked)
  T/T (  1 row)  → 'optional'    (RoP.151 — opt-in deadline,
                                  ☐ pre-unchecked per mig 068)
  F/T (  0 rows) → 'recommended' (defensive; no live data)
  F/F ( 18 rows) → 'recommended' (situational filings —
                                  Berufungserwiderung, Replik,
                                  Duplik, R.19 Preliminary
                                  Objection, R.116 EPÜ, etc.)

Why NOT msg 1746's mapping:
  - T/T → 'recommended' would PRE-CHECK RoP.151 in the save modal
    and auto-create a Kostenentscheidung deadline the user didn't
    ask for. That's the regression we'd ship.
  - F/F → 'informational' would render 18 real filing deadlines
    NEVER-SAVEABLE per design §2.3 ("informational … NEVER saves
    as a deadline"). They'd disappear from save flows entirely.

T/F branch is intentionally skipped — mig 078 already defaults
priority='mandatory', so all 153 T/F rows are already correct.
Writing 153 needless audit rows would dilute the backfill trail.

Audit-reason cites design §2.3 — that's the persistent rationale
captured in paliad.deadline_rule_audit. Migration enforces NOT NULL
post-run via a DO block that RAISE EXCEPTION on stragglers.
2026-05-15 00:28:49 +02:00

111 lines
5.4 KiB
SQL

-- t-paliad-183 / Fristen Phase 3 Slice 2 Step B-2 — backfill
-- paliad.deadline_rules.priority from the legacy (is_mandatory,
-- is_optional) pair per DESIGN §2.3 (NOT the inverted mapping in
-- head's msg 1746 — head's clarification msg 1750 rules in favour of
-- the design doc).
--
-- Final mapping (design §2.3 + RoP.151 / mig 068 t-paliad-157 semantic):
--
-- is_mandatory=true, is_optional=false → 'mandatory' (statutory must,
-- ☑ pre-checked in
-- save modal)
-- is_mandatory=true, is_optional=true → 'optional' (statutorily strict
-- ONCE IT APPLIES,
-- but applies only
-- if a party files —
-- RoP.151 is the
-- canonical case;
-- ☐ pre-unchecked)
-- is_mandatory=false, is_optional=true → 'recommended' (no live data, but
-- defensive default
-- so the CHECK
-- constraint stays
-- satisfied if such
-- a row ever lands)
-- is_mandatory=false, is_optional=false → 'recommended' (situational filings
-- — Berufungserwiderung,
-- Replik, Duplik,
-- R.19 Preliminary
-- Objection, R.116
-- EPÜ, Anschluss-
-- berufung, etc.
-- Default-save with
-- override, not
-- 'informational'
-- which would make
-- them never-saveable)
--
-- Live-data expected delta (172 rules total, mig 078 set every row to
-- the default 'mandatory'):
-- T/F (153 rows) → 'mandatory' — 153 no-op UPDATEs (already correct)
-- T/T ( 1 row) → 'optional' — 1 row flips
-- F/F ( 18 rows) → 'recommended' — 18 rows flip
-- F/T ( 0 rows) → 'recommended' — 0 rows (no live data)
--
-- The UPDATE is split into branches with explicit WHERE clauses so the
-- audit log records each branch as a distinct backfill action (separate
-- audit row chains by (is_mandatory, is_optional) shape). It also keeps
-- the migration idempotent: re-running only touches rows whose priority
-- doesn't already match the target.
--
-- Audit-reason cites design §2.3 — that's the persistent rationale in
-- the paliad.deadline_rule_audit log.
SELECT set_config(
'paliad.audit_reason',
'backfill 083: priority from (is_mandatory, is_optional) per design §2.3 — '
|| 'T/T→optional (RoP.151), F/F→recommended (situational filings)',
true);
-- Branch 1: T/T → 'optional' (RoP.151).
UPDATE paliad.deadline_rules
SET priority = 'optional'
WHERE is_mandatory = true
AND is_optional = true
AND priority <> 'optional';
-- Branch 2: F/F → 'recommended'.
UPDATE paliad.deadline_rules
SET priority = 'recommended'
WHERE is_mandatory = false
AND is_optional = false
AND priority <> 'recommended';
-- Branch 3: F/T → 'recommended' (defensive; no live rows today).
UPDATE paliad.deadline_rules
SET priority = 'recommended'
WHERE is_mandatory = false
AND is_optional = true
AND priority <> 'recommended';
-- Branch 4: T/F → 'mandatory'. Skipped explicitly: the mig 078 column
-- default is already 'mandatory', so every T/F row already has the
-- correct value. A defensive UPDATE here would write 153 needless
-- audit rows. Leave T/F untouched.
DO $$
DECLARE
n_mand int;
n_opt int;
n_reco int;
n_info int;
n_null int;
BEGIN
SELECT count(*) FILTER (WHERE priority = 'mandatory'),
count(*) FILTER (WHERE priority = 'optional'),
count(*) FILTER (WHERE priority = 'recommended'),
count(*) FILTER (WHERE priority = 'informational'),
count(*) FILTER (WHERE priority IS NULL)
INTO n_mand, n_opt, n_reco, n_info, n_null
FROM paliad.deadline_rules;
RAISE NOTICE 'backfill 083: priority distribution — '
'mandatory=%, optional=%, recommended=%, informational=%, NULL=%',
n_mand, n_opt, n_reco, n_info, n_null;
-- Hard assertion: priority is NOT NULL by schema (mig 078) and
-- every value must lie in the CHECK enum. n_null must be 0.
IF n_null > 0 THEN
RAISE EXCEPTION 'backfill 083: % rows still have priority IS NULL — '
'schema violation', n_null;
END IF;
END $$;