diff --git a/internal/db/migrations/133_upc_dmgs_pi_court_followup.down.sql b/internal/db/migrations/133_upc_dmgs_pi_court_followup.down.sql new file mode 100644 index 0000000..f11266c --- /dev/null +++ b/internal/db/migrations/133_upc_dmgs_pi_court_followup.down.sql @@ -0,0 +1,33 @@ +-- Reverses mig 133. Removes the 5 new rules: +-- * upc.dmgs.cfi.interim +-- * upc.dmgs.cfi.oral +-- * upc.dmgs.cfi.decision +-- * upc.dmgs.cfi.appeal_spawn +-- * upc.pi.cfi.appeal_spawn +-- +-- The audit_reason is required by the mig 079 trigger for DELETE; +-- set_config at top supplies it. +-- +-- Idempotent — if a rule is already missing the DELETE matches zero +-- rows and the audit log records nothing extra. + +SELECT set_config( + 'paliad.audit_reason', + 'mig 133 (down): revert UPC Damages tree-end rows and UPC PI appeal-spawn (t-paliad-285 / m/paliad#117 + t-paliad-286 / m/paliad#118)', + true); + +-- Delete the spawn rows first so the parent_id reference goes away +-- before the parent decision row is removed. +DELETE FROM paliad.deadline_rules + WHERE submission_code IN ( + 'upc.dmgs.cfi.appeal_spawn', + 'upc.pi.cfi.appeal_spawn') + AND lifecycle_state = 'published'; + +DELETE FROM paliad.deadline_rules + WHERE submission_code IN ( + 'upc.dmgs.cfi.interim', + 'upc.dmgs.cfi.oral', + 'upc.dmgs.cfi.decision') + AND proceeding_type_id = 17 + AND lifecycle_state = 'published'; diff --git a/internal/db/migrations/133_upc_dmgs_pi_court_followup.up.sql b/internal/db/migrations/133_upc_dmgs_pi_court_followup.up.sql new file mode 100644 index 0000000..e59fed9 --- /dev/null +++ b/internal/db/migrations/133_upc_dmgs_pi_court_followup.up.sql @@ -0,0 +1,405 @@ +-- t-paliad-285 (m/paliad#117) + t-paliad-286 (m/paliad#118) — +-- post-submission court followup for UPC Damages and appeal route +-- for UPC Provisional Measures. +-- +-- m's 2026-05-25 report: the upc.dmgs.cfi proceeding stops at the +-- last party submission (rejoin) — no interim conference, no oral +-- hearing, no decision row, no appeal-spawn. The upc.pi.cfi +-- proceeding has its decision row (`pi.order`) but no spawn into +-- the appeal tree. Both gaps prevent the Verfahrensablauf timeline +-- from rendering the court phase plus any downstream appeal sub- +-- tree that atlas's #96 spawn-rendering mechanism is otherwise +-- ready to surface. +-- +-- Two sections in one migration (slot 133 — knuth on 132, paliadin +-- coordinated): +-- +-- A. UPC Damages tree-end rows (#117) +-- A1 upc.dmgs.cfi.interim UPC RoP R.105 court-set hearing +-- A2 upc.dmgs.cfi.oral UPC RoP R.118 / R.250 court-set hearing +-- A3 upc.dmgs.cfi.decision UPC RoP R.118 / R.144 court-set decision +-- A4 upc.dmgs.cfi.appeal_spawn UPC RoP R.220.1(a) / R.224.1(a) 2mo, spawn → upc.apl.merits (id=11) +-- +-- B. UPC Provisional Measures appeal route (#118) +-- B1 upc.pi.cfi.appeal_spawn UPC RoP R.220.1(a) / R.224.1(a) 2mo, spawn → upc.apl.merits (id=11) +-- +-- Source citations: +-- * docs/research-deadlines-completeness-2026-05-25.md +-- — §2.1 (upc.dmgs.cfi has only 4 rules: R.131.2 / R.137.2 / R.139) +-- — §D Damages table (R.144 tree-end row missing — listed +-- in Tier 4 as "cosmetic", upgraded to Tier-0 by m's +-- report once the wider follow-up gap was understood) +-- * docs/audit-upc-rop-deadlines-2026-05-08.md §D row R.144, +-- §F R.220.1(a) / R.224.1(a) (verified verbatim in youpc DB +-- under law_type=UPCRoP). +-- * UPC Rules of Procedure (consolidated): +-- R.105 — Interim conference (court fixes after written +-- procedure closes; same structural shape as the inf +-- interim conference, already modelled as `upc.inf.cfi.interim`). +-- R.118 — Decision after oral hearing; general rule for +-- deciding panels. +-- R.250 — Determination of damages decision; damages- +-- specific decision rule (chains R.144 indication → +-- damages award). +-- R.144 — Final decision on damages quantum (tree-end +-- anchor for §A3). +-- R.220.1(a) — Appeal lies from any final decision / +-- decision disposing of the case at first instance. +-- A PI order under R.211 disposes of the urgent question +-- and is therefore appealable on the main 2-month track +-- (not the 15-day order track of R.220.1(c), which covers +-- case-management and procedural orders requiring leave). +-- Curie's §F table confirms the main-track wiring for +-- decisions on merits / disposing orders. +-- R.224.1(a) — Statement of Appeal within 2 months of +-- service of the final decision; the deadline-notes text +-- mirrors mig 095's inf.appeal_spawn / rev.appeal_spawn. +-- R.224.2(a) — Statement of grounds within 4 months +-- (separate deadline in the spawned upc.apl.merits +-- proceeding; already present as upc.apl.merits.grounds). +-- +-- Shape decisions (mirroring mig 012 / mig 095 conventions): +-- * Court-set rows (interim / oral / decision) carry +-- primary_party='court', event_type='hearing'|'decision', +-- duration_value=0, is_court_set=true, parent_id=NULL, +-- concept_id reuses the shared concepts already wired for +-- upc.inf.cfi (interim-conference / oral-hearing / decision). +-- * Spawn rows carry primary_party='both', is_spawn=true, +-- spawn_proceeding_type_id=11 (upc.apl.merits), spawn_label +-- identical to the merits spawn already in production. The +-- spawn row's parent_id is the spawning decision/order row +-- (so the audit log carries the trigger link). +-- * No condition_expr — m's F2.3 decision recorded in mig 095 +-- §3: "the appeal deadline should always be triggered by a +-- decision … appeal is always a possibility." Visibility +-- filtering on the frontend hides appeals on projects where +-- no appeal is contemplated. +-- * sequence_order numbering follows the inf convention +-- (40=interim, 50=oral, 60=decision, 80=appeal_spawn) so the +-- Verfahrensablauf timeline orders consistently across +-- proceedings. For PI the existing pi.order sits at +-- sequence_order=3; the appeal_spawn lands at 10 (clear of +-- the writ phase, room for future court-phase rows). +-- +-- Idempotency: every INSERT is gated by `WHERE NOT EXISTS (… same +-- submission_code, proceeding_type_id, lifecycle_state)`. Re-apply +-- against an already-migrated DB inserts zero rows and the audit +-- log carries no duplicate entries. +-- +-- audit_reason set_config required at the top — the mig 079 trigger +-- on paliad.deadline_rules raises EXCEPTION 'audit reason required' +-- on INSERT/UPDATE/DELETE without it. + +SELECT set_config( + 'paliad.audit_reason', + 'mig 133: t-paliad-285 / m/paliad#117 + t-paliad-286 / m/paliad#118 — UPC Damages tree-end rows (interim conference R.105, oral hearing R.118/R.250, decision R.118/R.144, appeal-spawn R.220.1(a)) and UPC Provisional Measures appeal-spawn R.220.1(a); see docs/research-deadlines-completeness-2026-05-25.md §D and docs/audit-upc-rop-deadlines-2026-05-08.md §D/§F', + true); + +-- ============================================================================= +-- A. UPC Damages — court-phase tree end (m/paliad#117) +-- ============================================================================= + +-- A1. upc.dmgs.cfi.interim — Interim conference (UPC RoP R.105). +-- Court-set hearing fixed by the judge-rapporteur once the +-- written procedure closes. Identical shape to +-- upc.inf.cfi.interim; reuses the shared interim-conference +-- concept node. +INSERT INTO paliad.deadline_rules + (proceeding_type_id, parent_id, submission_code, name, name_en, + description, primary_party, event_type, + duration_value, duration_unit, timing, + rule_code, deadline_notes, deadline_notes_en, sequence_order, + is_spawn, spawn_proceeding_type_id, spawn_label, + is_active, legal_source, is_bilateral, + condition_expr, priority, is_court_set, lifecycle_state, + concept_id) +SELECT + 17, + NULL, + 'upc.dmgs.cfi.interim', + 'Zwischenverfahren', + 'Interim Conference', + NULL, + 'court', + 'hearing', + 0, + 'months', + 'after', + NULL, + 'Termin vom Gericht bestimmt', + 'Date set by the court', + 40, + false, + NULL, + NULL, + true, + NULL, + false, + NULL, + 'optional', + true, + 'published', + 'e5071152-d408-4455-b644-9e79d86fd538' +WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules + WHERE submission_code = 'upc.dmgs.cfi.interim' + AND proceeding_type_id = 17 + AND lifecycle_state = 'published'); + +-- A2. upc.dmgs.cfi.oral — Oral hearing (UPC RoP R.118 / R.250). +-- Court-set hearing after the interim conference / close of +-- written procedure. Same shape as upc.inf.cfi.oral; reuses +-- the shared oral-hearing concept node. +INSERT INTO paliad.deadline_rules + (proceeding_type_id, parent_id, submission_code, name, name_en, + description, primary_party, event_type, + duration_value, duration_unit, timing, + rule_code, deadline_notes, deadline_notes_en, sequence_order, + is_spawn, spawn_proceeding_type_id, spawn_label, + is_active, legal_source, is_bilateral, + condition_expr, priority, is_court_set, lifecycle_state, + concept_id) +SELECT + 17, + NULL, + 'upc.dmgs.cfi.oral', + 'Mündliche Verhandlung', + 'Oral Hearing', + NULL, + 'court', + 'hearing', + 0, + 'months', + 'after', + NULL, + NULL, + NULL, + 50, + false, + NULL, + NULL, + true, + NULL, + false, + NULL, + 'optional', + true, + 'published', + 'd6e5b793-dcf1-4d83-81ff-34f42dbb3693' +WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules + WHERE submission_code = 'upc.dmgs.cfi.oral' + AND proceeding_type_id = 17 + AND lifecycle_state = 'published'); + +-- A3. upc.dmgs.cfi.decision — Damages decision (UPC RoP R.118 / +-- R.144 / R.250). Court-set decision delivered after oral +-- hearing; closes the §3.1 audit gap (R.144 tree-end). Same +-- shape as upc.inf.cfi.decision; reuses the shared decision +-- concept node. +INSERT INTO paliad.deadline_rules + (proceeding_type_id, parent_id, submission_code, name, name_en, + description, primary_party, event_type, + duration_value, duration_unit, timing, + rule_code, deadline_notes, deadline_notes_en, sequence_order, + is_spawn, spawn_proceeding_type_id, spawn_label, + is_active, legal_source, is_bilateral, + condition_expr, priority, is_court_set, lifecycle_state, + concept_id) +SELECT + 17, + NULL, + 'upc.dmgs.cfi.decision', + 'Entscheidung', + 'Decision', + NULL, + 'court', + 'decision', + 0, + 'months', + 'after', + NULL, + NULL, + NULL, + 60, + false, + NULL, + NULL, + true, + NULL, + false, + NULL, + 'optional', + true, + 'published', + '472fc32d-cc4f-4aa4-8ace-e422031812de' +WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules + WHERE submission_code = 'upc.dmgs.cfi.decision' + AND proceeding_type_id = 17 + AND lifecycle_state = 'published'); + +-- A4. upc.dmgs.cfi.appeal_spawn — Appeal against damages decision +-- (UPC RoP R.220.1(a), 2-month main track; grounds R.224.2(a) +-- run as a separate deadline in the spawned upc.apl.merits +-- proceeding). Parent points at the freshly-inserted +-- upc.dmgs.cfi.decision; the SELECT subquery resolves it +-- after A3 lands. Same shape as the mig 095 inf.appeal_spawn. +INSERT INTO paliad.deadline_rules + (proceeding_type_id, parent_id, submission_code, name, name_en, + description, primary_party, event_type, + duration_value, duration_unit, timing, + rule_code, deadline_notes, deadline_notes_en, sequence_order, + is_spawn, spawn_proceeding_type_id, spawn_label, + is_active, legal_source, is_bilateral, + condition_expr, priority, is_court_set, lifecycle_state) +SELECT + 17, + (SELECT id FROM paliad.deadline_rules + WHERE submission_code = 'upc.dmgs.cfi.decision' + AND proceeding_type_id = 17 + AND lifecycle_state = 'published' + AND is_active = true), + 'upc.dmgs.cfi.appeal_spawn', + 'Berufung gegen Schadensentscheidung', + 'Appeal against damages decision', + 'Berufung gegen die Entscheidung über die Schadensbemessung (R.118 / R.144). Statutarische Frist von 2 Monaten ab Zustellung der Entscheidung (R.224.1(a)); die Berufungsbegründung folgt mit 4 Monaten ab Zustellung (R.224.2(a), eigenständige Frist im Berufungsverfahren).', + 'both', + 'filing', + 2, + 'months', + 'after', + 'RoP.220.1.a', + 'Innerhalb von 2 Monaten ab Zustellung der Schadensentscheidung Berufungsschrift einreichen (R.224.1(a)). Die Berufungsbegründung (R.224.2(a), 4 Monate) läuft als separate Frist im Berufungsverfahren.', + 'Within 2 months of service of the damages decision lodge the Statement of appeal (R.224.1(a)). The Statement of grounds (R.224.2(a), 4 months) runs as an independent deadline in the appeal proceeding.', + 80, + true, + 11, + 'Berufungsverfahren öffnen', + true, + 'UPC.RoP.220.1', + false, + NULL, + 'optional', + false, + 'published' +WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules + WHERE submission_code = 'upc.dmgs.cfi.appeal_spawn' + AND proceeding_type_id = 17 + AND lifecycle_state = 'published'); + +-- ============================================================================= +-- B. UPC Provisional Measures — appeal route (m/paliad#118) +-- ============================================================================= + +-- B1. upc.pi.cfi.appeal_spawn — Appeal against PI order (UPC RoP +-- R.220.1(a), 2-month main track). PI orders under R.211 +-- dispose of the urgent question and are appealable on the +-- main 2-month track (R.220.1(a)/R.224.1(a)); the 15-day +-- order track of R.220.1(c) is for case-management / +-- procedural orders requiring leave and does not apply to +-- PI dispositions. Parent points at the existing +-- upc.pi.cfi.order (sequence_order=3) so the spawn fires +-- once the order is anchored on a project's timeline. +INSERT INTO paliad.deadline_rules + (proceeding_type_id, parent_id, submission_code, name, name_en, + description, primary_party, event_type, + duration_value, duration_unit, timing, + rule_code, deadline_notes, deadline_notes_en, sequence_order, + is_spawn, spawn_proceeding_type_id, spawn_label, + is_active, legal_source, is_bilateral, + condition_expr, priority, is_court_set, lifecycle_state) +SELECT + 10, + (SELECT id FROM paliad.deadline_rules + WHERE submission_code = 'upc.pi.cfi.order' + AND proceeding_type_id = 10 + AND lifecycle_state = 'published' + AND is_active = true), + 'upc.pi.cfi.appeal_spawn', + 'Berufung gegen Anordnung', + 'Appeal against PI order', + 'Berufung gegen die einstweilige Anordnung nach R.211. Eine PI-Anordnung erledigt die einstweilige Streitfrage und wird wie eine Endentscheidung im Hauptverfahren behandelt: statutarische Frist von 2 Monaten ab Zustellung (R.224.1(a)); die Berufungsbegründung folgt mit 4 Monaten ab Zustellung (R.224.2(a), eigenständige Frist im Berufungsverfahren). Die 15-Tage-Spur nach R.220.1(c) / R.220.2 gilt für Verfahrensanordnungen mit Zulassung und ist hier nicht einschlägig.', + 'both', + 'filing', + 2, + 'months', + 'after', + 'RoP.220.1.a', + 'Innerhalb von 2 Monaten ab Zustellung der PI-Anordnung Berufungsschrift einreichen (R.224.1(a)). Die Berufungsbegründung (R.224.2(a), 4 Monate) läuft als separate Frist im Berufungsverfahren.', + 'Within 2 months of service of the PI order lodge the Statement of appeal (R.224.1(a)). The Statement of grounds (R.224.2(a), 4 months) runs as an independent deadline in the appeal proceeding.', + 10, + true, + 11, + 'Berufungsverfahren öffnen', + true, + 'UPC.RoP.220.1', + false, + NULL, + 'optional', + false, + 'published' +WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules + WHERE submission_code = 'upc.pi.cfi.appeal_spawn' + AND proceeding_type_id = 10 + AND lifecycle_state = 'published'); + +-- ============================================================================= +-- C. Post-insert verification — raise if any expected row is missing +-- (matches the mig 095 / 127 convention; protects against a future +-- re-shape of the table that silently drops one of the WHERE NOT +-- EXISTS predicates). +-- ============================================================================= + +DO $$ +DECLARE + v_missing text; +BEGIN + SELECT string_agg(expected, ', ' ORDER BY expected) + INTO v_missing + FROM (VALUES + ('upc.dmgs.cfi.interim'), + ('upc.dmgs.cfi.oral'), + ('upc.dmgs.cfi.decision'), + ('upc.dmgs.cfi.appeal_spawn'), + ('upc.pi.cfi.appeal_spawn') + ) AS t(expected) + WHERE NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules dr + WHERE dr.submission_code = t.expected + AND dr.lifecycle_state = 'published' + AND dr.is_active = true); + + IF v_missing IS NOT NULL THEN + RAISE EXCEPTION + 'mig 133: expected published rules missing after insert: %', v_missing; + END IF; + + IF NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules dr + WHERE dr.submission_code = 'upc.dmgs.cfi.appeal_spawn' + AND dr.proceeding_type_id = 17 + AND dr.spawn_proceeding_type_id = 11 + AND dr.is_spawn = true + AND dr.parent_id IS NOT NULL + AND dr.lifecycle_state = 'published' + ) THEN + RAISE EXCEPTION + 'mig 133: upc.dmgs.cfi.appeal_spawn shape check failed (expected is_spawn=true, spawn_proceeding_type_id=11, parent_id set)'; + END IF; + + IF NOT EXISTS ( + SELECT 1 FROM paliad.deadline_rules dr + WHERE dr.submission_code = 'upc.pi.cfi.appeal_spawn' + AND dr.proceeding_type_id = 10 + AND dr.spawn_proceeding_type_id = 11 + AND dr.is_spawn = true + AND dr.parent_id IS NOT NULL + AND dr.lifecycle_state = 'published' + ) THEN + RAISE EXCEPTION + 'mig 133: upc.pi.cfi.appeal_spawn shape check failed (expected is_spawn=true, spawn_proceeding_type_id=11, parent_id set)'; + END IF; +END $$;