diff --git a/internal/db/migrations/096_proceeding_code_rename.down.sql b/internal/db/migrations/096_proceeding_code_rename.down.sql new file mode 100644 index 0000000..5461a07 --- /dev/null +++ b/internal/db/migrations/096_proceeding_code_rename.down.sql @@ -0,0 +1,99 @@ +-- Reverses mig 096. Restores the original UPPER_SNAKE codes on +-- paliad.proceeding_types + paliad.event_category_concepts, drops the +-- new upc.ccr.cfi row, removes the shape CHECK, refreshes the +-- deadline_search materialized view, then drops the snapshot table. +-- +-- audit_reason wrapper required by the mig 079 audit trigger. + +SELECT set_config( + 'paliad.audit_reason', + 'mig 096 (down): revert t-paliad-206 proceeding-code rename — restore UPPER_SNAKE codes from proceeding_types_pre_096, delete upc.ccr.cfi peer, drop shape CHECK', + true); + +-- ============================================================================= +-- 1. Drop the shape CHECK first so the UPPER_SNAKE restores don't trip it. +-- ============================================================================= + +ALTER TABLE paliad.proceeding_types + DROP CONSTRAINT IF EXISTS paliad_proceeding_code_shape; + +-- ============================================================================= +-- 2. Delete the upc.ccr.cfi peer. The down restores the pre-096 state, which +-- didn't have this row. If the row is already missing, the DELETE +-- matches zero — idempotent. +-- ============================================================================= + +DELETE FROM paliad.proceeding_types + WHERE code = 'upc.ccr.cfi'; + +-- ============================================================================= +-- 3. Restore proceeding_types.code from the pre_096 snapshot. The snapshot +-- captured the rows at first up-migration run; if the table is missing +-- (down run before up), the restore is a no-op. +-- ============================================================================= + +DO $$ +DECLARE + v_snap_exists boolean; +BEGIN + SELECT EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = 'paliad' + AND table_name = 'proceeding_types_pre_096' + ) INTO v_snap_exists; + + IF NOT v_snap_exists THEN + RAISE NOTICE + 'mig 096 (down): snapshot table paliad.proceeding_types_pre_096 missing — nothing to restore'; + RETURN; + END IF; + + UPDATE paliad.proceeding_types pt + SET code = snap.code + FROM paliad.proceeding_types_pre_096 snap + WHERE pt.id = snap.id + AND pt.code <> snap.code; +END $$; + +-- ============================================================================= +-- 4. Revert soft references on event_category_concepts.proceeding_type_code +-- by running the inverse mapping. Symmetric with §4 of the up migration. +-- ============================================================================= + +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_INF' WHERE proceeding_type_code = 'upc.inf.cfi'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_REV' WHERE proceeding_type_code = 'upc.rev.cfi'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_PI' WHERE proceeding_type_code = 'upc.pi.cfi'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_APP' WHERE proceeding_type_code = 'upc.apl.merits'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_DAMAGES' WHERE proceeding_type_code = 'upc.dmgs.cfi'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_DISCOVERY' WHERE proceeding_type_code = 'upc.disc.cfi'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_COST_APPEAL' WHERE proceeding_type_code = 'upc.apl.cost'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'UPC_APP_ORDERS' WHERE proceeding_type_code = 'upc.apl.order'; + +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'DE_INF' WHERE proceeding_type_code = 'de.inf.lg'; +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' WHERE proceeding_type_code = 'de.null.bpatg'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'DE_NULL_BGH' WHERE proceeding_type_code = 'de.null.bgh'; + +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'EP_GRANT' WHERE proceeding_type_code = 'epa.grant.exa'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'EPA_OPP' WHERE proceeding_type_code = 'epa.opp.opd'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'EPA_APP' WHERE proceeding_type_code = 'epa.opp.boa'; + +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'DPMA_OPP' WHERE proceeding_type_code = 'dpma.opp.dpma'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'DPMA_BPATG_BESCHWERDE' WHERE proceeding_type_code = 'dpma.appeal.bpatg'; +UPDATE paliad.event_category_concepts SET proceeding_type_code = 'DPMA_BGH_RB' WHERE proceeding_type_code = 'dpma.appeal.bgh'; + +-- ============================================================================= +-- 5. Refresh deadline_search so the reverted proceeding_code strings +-- repopulate the materialized view. +-- ============================================================================= + +REFRESH MATERIALIZED VIEW paliad.deadline_search; + +-- ============================================================================= +-- 6. Drop the snapshot table so a re-applied up migration captures a +-- fresh snapshot of the current state. +-- ============================================================================= + +DROP TABLE IF EXISTS paliad.proceeding_types_pre_096; diff --git a/internal/db/migrations/096_proceeding_code_rename.up.sql b/internal/db/migrations/096_proceeding_code_rename.up.sql new file mode 100644 index 0000000..e15e398 --- /dev/null +++ b/internal/db/migrations/096_proceeding_code_rename.up.sql @@ -0,0 +1,226 @@ +-- 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 $$;