-- t-paliad-206 / proceeding-code rename — replace the historical -- UPPER_SNAKE proceeding codes with the lowercase dot-separated -- taxonomy ratified by m on 2026-05-18 (see -- docs/design-proceeding-code-taxonomy-2026-05-18.md). -- -- IDs are stable. Only the `code` STRING changes. FKs -- (deadline_rules.proceeding_type_id, projects.proceeding_type_id, -- deadline_rules.spawn_proceeding_type_id) reference IDs, so the -- existing rule corpus and spawn wiring continue to work unchanged -- (incl. mig 095's spawn_proceeding_type_id=11 which becomes -- 'upc.apl.merits' after this migration). -- -- Soft references on `code` (text column on event_category_concepts) are -- updated row-for-row to keep the soft join through proceeding_types.code -- resolving. -- -- The materialized view paliad.deadline_search projects pt.code as -- proceeding_code; mig 096 REFRESHes it at the bottom so the new codes -- show up in search results immediately. -- -- Idempotent: -- * UPDATEs are guarded by `WHERE code = ''`. Re-running after a -- successful first apply is a no-op. -- * INSERT of upc.ccr.cfi uses `WHERE NOT EXISTS` keyed on the new -- code (bohr noted in t-paliad-205 that a UNIQUE constraint on the -- code column is not present, hence WHERE NOT EXISTS rather than -- ON CONFLICT). -- * CHECK constraint is dropped-then-recreated under the same name -- (paliad_proceeding_code_shape) so reapplication doesn't error. -- * Snapshot table uses CREATE TABLE IF NOT EXISTS. -- -- audit_reason wrapper required by the mig 079 audit trigger. SELECT set_config( 'paliad.audit_reason', 'mig 096: t-paliad-206 proceeding-code rename — lowercase dot-separated taxonomy + new upc.ccr.cfi illustrative peer; see docs/design-proceeding-code-taxonomy-2026-05-18.md', true); -- ============================================================================= -- 1. Backup snapshot of paliad.proceeding_types BEFORE the rename. The -- rename is forward-only in code (the Go + frontend sweeps reference -- the new strings) but the DB snapshot is the audit anchor and the -- source for the down migration. -- ============================================================================= CREATE TABLE IF NOT EXISTS paliad.proceeding_types_pre_096 AS SELECT *, now() AS snapshotted_at FROM paliad.proceeding_types; COMMENT ON TABLE paliad.proceeding_types_pre_096 IS 'Snapshot of paliad.proceeding_types taken before mig 096 renamed ' 'the `code` strings to the lowercase dot-separated taxonomy ' '(t-paliad-206, 2026-05-18). Source-of-truth for the down ' 'migration; persists post-rename as the permanent audit record.'; -- ============================================================================= -- 2. Drop any prior shape CHECK so we can recreate it post-rename. The -- constraint name is stable so reapplication idempotently drops it. -- ============================================================================= ALTER TABLE paliad.proceeding_types DROP CONSTRAINT IF EXISTS paliad_proceeding_code_shape; -- ============================================================================= -- 3. The 19 renames. Order-independent — every UPDATE is guarded by -- `WHERE code = ''` so re-application is a no-op. id values in -- the trailing comment for cross-reference with the design doc. -- ============================================================================= -- UPC UPDATE paliad.proceeding_types SET code = 'upc.inf.cfi' WHERE code = 'UPC_INF'; -- id=8 UPDATE paliad.proceeding_types SET code = 'upc.rev.cfi' WHERE code = 'UPC_REV'; -- id=9 UPDATE paliad.proceeding_types SET code = 'upc.pi.cfi' WHERE code = 'UPC_PI'; -- id=10 UPDATE paliad.proceeding_types SET code = 'upc.apl.merits' WHERE code = 'UPC_APP'; -- id=11 UPDATE paliad.proceeding_types SET code = 'upc.dmgs.cfi' WHERE code = 'UPC_DAMAGES'; -- id=17 UPDATE paliad.proceeding_types SET code = 'upc.disc.cfi' WHERE code = 'UPC_DISCOVERY'; -- id=18 UPDATE paliad.proceeding_types SET code = 'upc.apl.cost' WHERE code = 'UPC_COST_APPEAL';-- id=19 UPDATE paliad.proceeding_types SET code = 'upc.apl.order' WHERE code = 'UPC_APP_ORDERS'; -- id=20 -- DE UPDATE paliad.proceeding_types SET code = 'de.inf.lg' WHERE code = 'DE_INF'; -- id=12 UPDATE paliad.proceeding_types SET code = 'de.inf.olg' WHERE code = 'DE_INF_OLG'; -- id=25 UPDATE paliad.proceeding_types SET code = 'de.inf.bgh' WHERE code = 'DE_INF_BGH'; -- id=26 UPDATE paliad.proceeding_types SET code = 'de.null.bpatg' WHERE code = 'DE_NULL'; -- id=13 UPDATE paliad.proceeding_types SET code = 'de.null.bgh' WHERE code = 'DE_NULL_BGH'; -- id=27 -- EPA UPDATE paliad.proceeding_types SET code = 'epa.grant.exa' WHERE code = 'EP_GRANT'; -- id=16 UPDATE paliad.proceeding_types SET code = 'epa.opp.opd' WHERE code = 'EPA_OPP'; -- id=14 UPDATE paliad.proceeding_types SET code = 'epa.opp.boa' WHERE code = 'EPA_APP'; -- id=15 -- DPMA UPDATE paliad.proceeding_types SET code = 'dpma.opp.dpma' WHERE code = 'DPMA_OPP'; -- id=28 UPDATE paliad.proceeding_types SET code = 'dpma.appeal.bpatg' WHERE code = 'DPMA_BPATG_BESCHWERDE';-- id=29 UPDATE paliad.proceeding_types SET code = 'dpma.appeal.bgh' WHERE code = 'DPMA_BGH_RB'; -- id=30 -- ============================================================================= -- 4. Update soft references on event_category_concepts.proceeding_type_code. -- Same OLD→NEW table as above; the column has a UNIQUE NULLS NOT -- DISTINCT constraint on (event_category_id, concept_id, proceeding_type_code) -- but no row has the NEW string yet so the UPDATEs cannot collide. -- ============================================================================= -- UPC UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.inf.cfi' WHERE proceeding_type_code = 'UPC_INF'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.rev.cfi' WHERE proceeding_type_code = 'UPC_REV'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.pi.cfi' WHERE proceeding_type_code = 'UPC_PI'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.apl.merits' WHERE proceeding_type_code = 'UPC_APP'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.dmgs.cfi' WHERE proceeding_type_code = 'UPC_DAMAGES'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.disc.cfi' WHERE proceeding_type_code = 'UPC_DISCOVERY'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.apl.cost' WHERE proceeding_type_code = 'UPC_COST_APPEAL'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'upc.apl.order' WHERE proceeding_type_code = 'UPC_APP_ORDERS'; -- DE UPDATE paliad.event_category_concepts SET proceeding_type_code = 'de.inf.lg' WHERE proceeding_type_code = 'DE_INF'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'de.inf.olg' WHERE proceeding_type_code = 'DE_INF_OLG'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'de.inf.bgh' WHERE proceeding_type_code = 'DE_INF_BGH'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'de.null.bpatg' WHERE proceeding_type_code = 'DE_NULL'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'de.null.bgh' WHERE proceeding_type_code = 'DE_NULL_BGH'; -- EPA UPDATE paliad.event_category_concepts SET proceeding_type_code = 'epa.grant.exa' WHERE proceeding_type_code = 'EP_GRANT'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'epa.opp.opd' WHERE proceeding_type_code = 'EPA_OPP'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'epa.opp.boa' WHERE proceeding_type_code = 'EPA_APP'; -- DPMA UPDATE paliad.event_category_concepts SET proceeding_type_code = 'dpma.opp.dpma' WHERE proceeding_type_code = 'DPMA_OPP'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'dpma.appeal.bpatg' WHERE proceeding_type_code = 'DPMA_BPATG_BESCHWERDE'; UPDATE paliad.event_category_concepts SET proceeding_type_code = 'dpma.appeal.bgh' WHERE proceeding_type_code = 'DPMA_BGH_RB'; -- ============================================================================= -- 5. Insert the new illustrative peer `upc.ccr.cfi`. is_active=true so it -- surfaces in the determinator + dropdowns; no rules attached. -- proceeding_mapping.go routes cascade hits on this code back to -- upc.inf.cfi (id=8) with the with_ccr default flag — see design doc S1. -- -- WHERE NOT EXISTS gates the insert on the new code so re-application -- is a no-op even though there's no UNIQUE constraint on (code). -- ============================================================================= INSERT INTO paliad.proceeding_types (code, category, jurisdiction, is_active, name, name_en, description) SELECT 'upc.ccr.cfi', 'fristenrechner', 'UPC', true, 'Widerklage auf Nichtigkeit', 'Counterclaim for Revocation', 'Illustrativer Peer von upc.inf.cfi für Widerklagen auf Nichtigkeit. Regeln liegen auf upc.inf.cfi (with_ccr=true); der Fristenrechner leitet bei Auswahl dorthin weiter. Keine eigenen Fristregeln.' WHERE NOT EXISTS ( SELECT 1 FROM paliad.proceeding_types WHERE code = 'upc.ccr.cfi'); -- ============================================================================= -- 6. CHECK constraint on the code shape. Active rows must conform to the -- new lowercase dot-separated form; the carve-out for -- `_archived_litigation` keeps the Pipeline-A bucket addressable. -- ============================================================================= ALTER TABLE paliad.proceeding_types ADD CONSTRAINT paliad_proceeding_code_shape CHECK ( code ~ '^[a-z]+\.[a-z]+\.[a-z]+$' OR code ~ '^_archived_' ); -- ============================================================================= -- 7. Refresh the deadline_search materialized view so search hits return -- the new proceeding_code strings immediately. -- ============================================================================= REFRESH MATERIALIZED VIEW paliad.deadline_search; -- ============================================================================= -- 8. Hard assertions. Half-applied migrations would leave the rule corpus -- inconsistent with the new shape; assert every active fristenrechner -- code conforms and that no old codes leak. -- ============================================================================= DO $$ DECLARE v_new_shape integer; v_old_codes integer; v_ccr_row integer; BEGIN -- 8.1 Every active fristenrechner row matches the new shape regex. -- 20 = 19 renamed rows + 1 newly inserted upc.ccr.cfi. The check -- uses >= so an additional row added in a follow-up migration -- doesn't trip the assertion. SELECT count(*) INTO v_new_shape FROM paliad.proceeding_types WHERE category = 'fristenrechner' AND is_active = true AND code ~ '^[a-z]+\.[a-z]+\.[a-z]+$'; IF v_new_shape < 20 THEN RAISE EXCEPTION 'mig 096: expected >= 20 active fristenrechner rows on the new shape, got %', v_new_shape; END IF; -- 8.2 No old UPPER_SNAKE codes remain on any row. SELECT count(*) INTO v_old_codes FROM paliad.proceeding_types WHERE code LIKE 'UPC\_%' ESCAPE '\' OR code LIKE 'DE\_%' ESCAPE '\' OR code LIKE 'EPA\_%' ESCAPE '\' OR code LIKE 'EP\_%' ESCAPE '\' OR code LIKE 'DPMA\_%' ESCAPE '\'; IF v_old_codes <> 0 THEN RAISE EXCEPTION 'mig 096: expected 0 old UPPER_SNAKE codes after rename, got %', v_old_codes; END IF; -- 8.3 The new ccr peer exists and is active. SELECT count(*) INTO v_ccr_row FROM paliad.proceeding_types WHERE code = 'upc.ccr.cfi' AND is_active = true; IF v_ccr_row <> 1 THEN RAISE EXCEPTION 'mig 096: expected 1 active upc.ccr.cfi row, got %', v_ccr_row; END IF; END $$;