Files
paliad/internal/db/migrations/127_wave0_tier0_deadline_fixes.up.sql
mAi 05f7ea2af5 mAi: #99 #94 - t-paliad-263 Wave 0 - Tier 0 deadline-rule corrections
Migration 127 lands curie's audit-doc Tier 0 sweep (docs/research-
deadlines-completeness-2026-05-25.md section 10) plus the UPC
Statement of Claim citation backfill from m/paliad#99.

14 single-row UPDATEs touching UPC + DE-LG + DPMA + EPA proceedings:

T0.1  upc.rev.cfi.defence      dur 3mo -> 2mo (RoP.049.1)
T0.2  upc.rev.cfi.rejoin       dur 2mo -> 1mo (RoP.052)
T0.3  upc.apl.merits.response  dur 2mo -> 3mo (RoP.235.1)
T0.4  de.inf.lg.beruf_begr     parent_id berufung -> NULL (ZPO 520.2)
T0.7  upc.rev.cfi.reply        citation backfill RoP.051
T0.9  upc.apl.merits.notice    citation RoP.220.1 -> RoP.224.1.a
T0.10 upc.apl.merits.grounds   citation RoP.220.1 -> RoP.224.2.a
T0.12 dpma.opp.dpma.erwiderung   flip is_court_set, drop PatG 59.3
T0.13 dpma.appeal.bpatg.begruendung flip is_court_set, drop PatG 75.1
T0.14 de.null.bpatg.erwidg     citation PatG 82.1 -> PatG 82.3
T0.15 de.null.bgh.begruendung  citation PatG 111.1 -> ZPO 520.2 (via PatG 117)
T0.16 de.null.bgh.erwiderung   flip is_court_set, recite as ZPO 521.2 (via PatG 117)
T0.17 epa.opp.opd.erwidg       flip is_court_set (EPO Guidelines D-IV 5.2)
#99   upc.inf.cfi.soc          backfill UPC RoP R.13(1) citation

T0.5 and T0.6 (de.inf.lg.replik / .duplik) shipped separately as
mig 124 (m/paliad#95). T0.8 / T0.11 dedup'd into T0.2 / T0.1 per
the audit doc.

Each UPDATE guarded by a WHERE clause matching only the pre-fix
row state (mig 095 convention) - re-apply against a DB carrying
the fix matches zero rows and no-ops, no duplicate deadline_rule_
audit entries on idempotent re-runs. Verification DO block at the
end RAISE EXCEPTIONs if any row remains in inconsistent state.

Applied to live youpc DB via Supabase MCP with audit_reason set
(13 rows touched - T0.4 also fired; all 14 verified in post-fix
shape via direct query). applied_migrations entry NOT pre-recorded;
the boot-time runner inserts version=127 cleanly on next deploy
because every guarded UPDATE no-ops at that point.

Build hygiene: go build / go test ./internal/... / bun run build
all clean (2824 i18n keys, no scan warnings). No code changes -
pure data migration.

Cites: UPC RoP (UPCRoP.013.1 / 049.1 / 051 / 052 / 224.1.a /
224.2.a / 235.1), PatG 82.3 / 117, ZPO 520.2 / 521.2, EPC R.79(1)
+ EPO Guidelines D-IV 5.2.
2026-05-25 15:56:19 +02:00

478 lines
21 KiB
SQL

-- t-paliad-263 Wave 0 + m/paliad#99 — Tier 0 deadline-rule corrections.
--
-- Source: docs/research-deadlines-completeness-2026-05-25.md §10 Tier 0
-- (curie's bulletproof completeness audit, merged 2026-05-25 as commit
-- 94a9e7e). 16 distinct single-row UPDATEs across UPC + DE-LG + DPMA +
-- EPA proceedings; T0.5 + T0.6 were shipped separately as mig 124
-- (m/paliad#95, de.inf.lg Replik/Duplik sequencing) and are not
-- repeated here. T0.8 (covered by T0.2) and T0.11 (covered by T0.1)
-- are dedup'd out per the audit's own note.
--
-- Also folds in m/paliad#99 (UPC Statement of Claim missing legal
-- citation): upc.inf.cfi.soc.rule_code / legal_source backfilled to
-- UPC RoP R.13(1). Same migration file, separate UPDATE block with
-- its own guard.
--
-- All fixes within the existing schema (no new columns). Each UPDATE
-- is guarded by a WHERE clause that matches only the pre-fix row
-- state (per mig 095 convention) — re-applying against a DB that
-- already carries the fix matches zero rows and no-ops, so there are
-- no duplicate deadline_rule_audit entries on idempotent re-runs.
--
-- Verification DO block at the end RAISEs EXCEPTION if any of the
-- patched rows is left in an inconsistent shape (mixing pre-fix and
-- post-fix state).
--
-- audit_reason set_config required at the top — the mig 079 trigger
-- on paliad.deadline_rules raises EXCEPTION 'audit reason required'
-- on any UPDATE without it.
--
-- Slot 127 reserved per paliadin: sequence is 124 brunel #95 (done),
-- 125 hermes #97, 126 icarus #80, 127 brunel Wave 0 + #99, 128+ next.
SELECT set_config(
'paliad.audit_reason',
'mig 127: t-paliad-263 Wave 0 + m/paliad#99 — Tier 0 deadline-rule corrections from curie''s audit (docs/research-deadlines-completeness-2026-05-25.md §10) plus UPC SoC R.13 citation',
true);
-- =============================================================================
-- T0.1 upc.rev.cfi.defence — duration 3mo → 2mo per RoP.049.1.
-- Zero-pads the rule_code citation to canonical form. Audit §5
-- (wrong period — every UPC_REV tracked in paliad today computes
-- Defence at +3 months, statute says +2). Verbatim from
-- UPCRoP.049.1: "The defendant shall lodge a Defence to revocation
-- within two months of service of the Statement for revocation."
-- =============================================================================
UPDATE paliad.deadline_rules
SET duration_value = 2,
rule_code = 'RoP.049.1',
updated_at = now()
WHERE submission_code = 'upc.rev.cfi.defence'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 3
AND rule_code = 'RoP.49.1';
-- =============================================================================
-- T0.2 upc.rev.cfi.rejoin — duration 2mo → 1mo per RoP.052; add citation.
-- Audit §5 (wrong period). Verbatim from UPCRoP.052: "Within one
-- month of the service of the Reply the defendant may lodge a
-- Rejoinder to the Reply to the Defence to revocation."
-- =============================================================================
UPDATE paliad.deadline_rules
SET duration_value = 1,
rule_code = 'RoP.052',
legal_source = 'UPC.RoP.52',
updated_at = now()
WHERE submission_code = 'upc.rev.cfi.rejoin'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 2
AND rule_code IS NULL;
-- =============================================================================
-- T0.3 upc.apl.merits.response — duration 2mo → 3mo per RoP.235.1.
-- Audit §5 (wrong period — every main-track appellate respondent).
-- Verbatim from UPCRoP.235.1: "Within three months of service of
-- the Statement of grounds of appeal pursuant to Rule 224.2(a),
-- any other party … may lodge a Statement of response."
-- =============================================================================
UPDATE paliad.deadline_rules
SET duration_value = 3,
rule_code = 'RoP.235.1',
legal_source = 'UPC.RoP.235.1',
updated_at = now()
WHERE submission_code = 'upc.apl.merits.response'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 2
AND rule_code IS NULL;
-- =============================================================================
-- T0.4 de.inf.lg.beruf_begr — parent_id = NULL (was de.inf.lg.berufung).
-- Audit §7.1 — every DE-LG-Verletzung appeal renders the
-- Berufungsbegründung at trigger + 1mo (Berufung) + 2mo = 3 months
-- from Urteil-service. Per ZPO §520(2) "die Frist für die
-- Berufungsbegründung beträgt zwei Monate. Sie beginnt mit der
-- Zustellung des in vollständiger Form abgefassten Urteils" → 2
-- months from Urteil-service (parallel to, not chained off, the
-- Berufungsfrist itself). NULL parent_id makes the rule anchor
-- on the proceeding's trigger date — matches how the symmetric
-- de.inf.olg.begruendung is modelled.
-- =============================================================================
UPDATE paliad.deadline_rules
SET parent_id = NULL,
updated_at = now()
WHERE submission_code = 'de.inf.lg.beruf_begr'
AND is_active = true
AND lifecycle_state = 'published'
AND parent_id = (
SELECT id FROM paliad.deadline_rules
WHERE submission_code = 'de.inf.lg.berufung'
AND is_active = true
AND lifecycle_state = 'published'
LIMIT 1
);
-- =============================================================================
-- T0.5 / T0.6 de.inf.lg.replik + de.inf.lg.duplik — already shipped
-- as mig 124 (m/paliad#95). Not repeated here. Idempotency of the
-- audit's Tier 0 sweep against a fresh DB is preserved because mig
-- 124 runs before this one and is itself guarded.
-- =============================================================================
-- =============================================================================
-- T0.7 upc.rev.cfi.reply — backfill rule_code + legal_source per RoP.051.
-- Audit §4.1 — duration (2mo) unchanged. Verbatim from UPCRoP.051:
-- "Reply to Defence to revocation and Application to amend the
-- patent. The claimant in the revocation action may, within two
-- months of service of the Defence to revocation and the
-- Application to amend the patent, if any, lodge a Reply…"
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = 'RoP.051',
legal_source = 'UPC.RoP.51',
updated_at = now()
WHERE submission_code = 'upc.rev.cfi.reply'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code IS NULL
AND legal_source IS NULL;
-- =============================================================================
-- T0.9 upc.apl.merits.notice — citation drift RoP.220.1 → RoP.224.1.a.
-- Audit §4.1 — duration unchanged. R.220.1 is the umbrella ("an
-- appeal may be brought"); R.224.1(a) carries the Notice-of-appeal
-- 2-month period explicitly.
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = 'RoP.224.1.a',
legal_source = 'UPC.RoP.224.1.a',
updated_at = now()
WHERE submission_code = 'upc.apl.merits.notice'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.220.1'
AND legal_source = 'UPC.RoP.220.1';
-- =============================================================================
-- T0.10 upc.apl.merits.grounds — citation drift RoP.220.1 → RoP.224.2.a.
-- Audit §4.1 — duration unchanged. R.224.2(a) sets the Grounds
-- 4-month period for decisions referred to in R.220.1(a) and (b).
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = 'RoP.224.2.a',
legal_source = 'UPC.RoP.224.2.a',
updated_at = now()
WHERE submission_code = 'upc.apl.merits.grounds'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.220.1'
AND legal_source = 'UPC.RoP.220.1';
-- =============================================================================
-- T0.12 dpma.opp.dpma.erwiderung — flip is_court_set = true; drop the
-- § 59(3) PatG citation. Audit §4.3 + §9.1: §59(3) addresses
-- Anhörung, not a 4-month response period. No statutory
-- Erwiderungsfrist exists in §59 — the 4-month figure is DPMA
-- practice (DPMA-Richtlinien D-IV 5.2). Modelled court-set, the
-- 4-month value remains the default-display heuristic the
-- lawyer overrides via "Datum setzen".
-- =============================================================================
UPDATE paliad.deadline_rules
SET is_court_set = true,
rule_code = NULL,
legal_source = NULL,
updated_at = now()
WHERE submission_code = 'dpma.opp.dpma.erwiderung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = false
AND legal_source = 'DE.PatG.59.3';
-- =============================================================================
-- T0.13 dpma.appeal.bpatg.begruendung — flip is_court_set = true; drop
-- the § 75 PatG citation. Audit §4.3 + §9.1: §75 PatG addresses
-- aufschiebende Wirkung only, not a Begründungsfrist. No fixed
-- Begründungsfrist for BPatG-Beschwerde exists in PatG §§73-80 —
-- the BPatG sets it in the individual case. 1-month default
-- retained as display heuristic.
-- =============================================================================
UPDATE paliad.deadline_rules
SET is_court_set = true,
rule_code = NULL,
legal_source = NULL,
updated_at = now()
WHERE submission_code = 'dpma.appeal.bpatg.begruendung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = false
AND legal_source = 'DE.PatG.75.1';
-- =============================================================================
-- T0.14 de.null.bpatg.erwidg — citation DE.PatG.82.1 → DE.PatG.82.3.
-- Audit §4.4 — duration (2 months) is correct. §82(1) carries the
-- 1-month Erklärungsfrist ("sich darüber zu erklären"); the full
-- Klageerwiderung 2-month period lives in §82(3).
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = '§ 82 Abs. 3 PatG',
legal_source = 'DE.PatG.82.3',
updated_at = now()
WHERE submission_code = 'de.null.bpatg.erwidg'
AND is_active = true
AND lifecycle_state = 'published'
AND legal_source = 'DE.PatG.82.1';
-- =============================================================================
-- T0.15 de.null.bgh.begruendung — citation DE.PatG.111.1 →
-- DE.ZPO.520.2 (via PatG §117). Audit §4.4 — duration (3 months)
-- is correct. §111 PatG defines the Grounds of Berufung
-- (Verletzung des Bundesrechts), not a Begründungsfrist; the
-- 3-month figure is supplied by §117 PatG → ZPO §520(2).
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = '§ 520 Abs. 2 ZPO i.V.m. § 117 PatG',
legal_source = 'DE.ZPO.520.2',
updated_at = now()
WHERE submission_code = 'de.null.bgh.begruendung'
AND is_active = true
AND lifecycle_state = 'published'
AND legal_source = 'DE.PatG.111.1';
-- =============================================================================
-- T0.16 de.null.bgh.erwiderung — flip is_court_set = true; recite as
-- DE.ZPO.521.2 (via PatG §117). Audit §4.4 + §9.1 — §111 PatG
-- has no Erwiderungsfrist clause. The actual Erwiderungsfrist
-- for BGH-Nichtigkeitsberufung is set by the court per §117
-- PatG → ZPO §521(2). 2-month default retained as display
-- heuristic.
-- =============================================================================
UPDATE paliad.deadline_rules
SET is_court_set = true,
rule_code = '§ 521 Abs. 2 ZPO i.V.m. § 117 PatG',
legal_source = 'DE.ZPO.521.2',
updated_at = now()
WHERE submission_code = 'de.null.bgh.erwiderung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = false
AND legal_source = 'DE.PatG.111.3';
-- =============================================================================
-- T0.17 epa.opp.opd.erwidg — flip is_court_set = true. Audit §4.5 +
-- §9.1: R.79(1) EPÜ authorises the Opposition Division to set
-- the period, but does not specify a fixed 4 months. The 4-month
-- figure is administrative practice (EPO Guidelines D-IV 5.2).
-- Citation retained as the rule-of-authority for the OD's
-- discretion. 4-month default retained as display heuristic.
-- =============================================================================
UPDATE paliad.deadline_rules
SET is_court_set = true,
updated_at = now()
WHERE submission_code = 'epa.opp.opd.erwidg'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = false
AND legal_source = 'EU.EPC-R.79.1';
-- =============================================================================
-- m/paliad#99 upc.inf.cfi.soc — backfill UPC RoP R.13(1) citation.
-- The Statement of Claim is defined in UPC RoP R.13 (R.13.1
-- lists the required contents). The row carries no statutory
-- deadline (duration_value = 0, parent_id IS NULL — the SoC is
-- the originating filing that anchors the proceeding's trigger
-- date), but the catalog UI surfaces the rule citation in
-- result cards and the Type=Statement-of-Claim / Rule=Auto
-- resolution; both render blank today because rule_code +
-- legal_source are NULL. Backfill leaves duration / anchor /
-- party untouched.
-- =============================================================================
UPDATE paliad.deadline_rules
SET rule_code = 'RoP.013.1',
legal_source = 'UPC.RoP.13.1',
updated_at = now()
WHERE submission_code = 'upc.inf.cfi.soc'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code IS NULL
AND legal_source IS NULL;
-- =============================================================================
-- Hard assertions. Each touched row must end up in its post-fix
-- shape. Re-running the migration after a successful first run is a
-- no-op for the data but the assertions still pass because they
-- check the post-fix state.
-- =============================================================================
DO $$
DECLARE
v_count integer;
BEGIN
-- T0.1 defence: dur=2 + canonical zero-padded rule_code
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.rev.cfi.defence'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 2
AND rule_code = 'RoP.049.1';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.1: upc.rev.cfi.defence not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.2 rejoin: dur=1
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.rev.cfi.rejoin'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 1
AND rule_code = 'RoP.052';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.2: upc.rev.cfi.rejoin not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.3 response: dur=3
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.apl.merits.response'
AND is_active = true
AND lifecycle_state = 'published'
AND duration_value = 3
AND rule_code = 'RoP.235.1';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.3: upc.apl.merits.response not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.4 beruf_begr: parent_id IS NULL
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'de.inf.lg.beruf_begr'
AND is_active = true
AND lifecycle_state = 'published'
AND parent_id IS NULL;
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.4: de.inf.lg.beruf_begr not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.7 reply: citation backfilled
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.rev.cfi.reply'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.051'
AND legal_source = 'UPC.RoP.51';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.7: upc.rev.cfi.reply not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.9 notice: citation RoP.224.1.a
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.apl.merits.notice'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.224.1.a'
AND legal_source = 'UPC.RoP.224.1.a';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.9: upc.apl.merits.notice not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.10 grounds: citation RoP.224.2.a
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.apl.merits.grounds'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.224.2.a'
AND legal_source = 'UPC.RoP.224.2.a';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.10: upc.apl.merits.grounds not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.12 dpma.opp erwiderung: court-set, no citation
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'dpma.opp.dpma.erwiderung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = true
AND legal_source IS NULL
AND rule_code IS NULL;
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.12: dpma.opp.dpma.erwiderung not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.13 dpma.appeal.bpatg begründung: court-set, no citation
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'dpma.appeal.bpatg.begruendung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = true
AND legal_source IS NULL
AND rule_code IS NULL;
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.13: dpma.appeal.bpatg.begruendung not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.14 bpatg erwidg: §82.3
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'de.null.bpatg.erwidg'
AND is_active = true
AND lifecycle_state = 'published'
AND legal_source = 'DE.PatG.82.3';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.14: de.null.bpatg.erwidg not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.15 bgh begründung: ZPO §520.2
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'de.null.bgh.begruendung'
AND is_active = true
AND lifecycle_state = 'published'
AND legal_source = 'DE.ZPO.520.2';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.15: de.null.bgh.begruendung not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.16 bgh erwiderung: court-set, ZPO §521.2
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'de.null.bgh.erwiderung'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = true
AND legal_source = 'DE.ZPO.521.2';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.16: de.null.bgh.erwiderung not in post-fix shape (got % matches)', v_count;
END IF;
-- T0.17 epa.opp opd erwidg: court-set
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'epa.opp.opd.erwidg'
AND is_active = true
AND lifecycle_state = 'published'
AND is_court_set = true;
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 T0.17: epa.opp.opd.erwidg not in post-fix shape (got % matches)', v_count;
END IF;
-- #99 upc.inf.cfi.soc: citation backfilled
SELECT count(*) INTO v_count
FROM paliad.deadline_rules
WHERE submission_code = 'upc.inf.cfi.soc'
AND is_active = true
AND lifecycle_state = 'published'
AND rule_code = 'RoP.013.1'
AND legal_source = 'UPC.RoP.13.1';
IF v_count <> 1 THEN
RAISE EXCEPTION 'mig 127 #99: upc.inf.cfi.soc not in post-fix shape (got % matches)', v_count;
END IF;
END $$;