Files
paliad/internal/db/migrations/009_seed_deadline_rules.up.sql
m 1b2ef28334 feat(db): Phase A — paliad schema, RLS, migrations, golang-migrate
Implements docs/design-kanzlai-integration.md §8 Phase A.

Schema (paliad.*):
- users (extends auth.users) with office, practice_group, role
- akten with visibility columns: owning_office, collaborators uuid[],
  firm_wide_visible (per design §2)
- parteien, fristen, termine, dokumente, akten_events, notizen
  (polymorphic notes; notizen_exactly_one_parent CHECK)
- proceeding_types, deadline_rules, holidays (reference data)
- 4 feedback tables re-namespaced from public.* into paliad.*
  (handler swap to direct DB is a follow-up; old public tables stay
  intact for now and continue serving via PostgREST)

Visibility (paliad.can_see_akte):
- single SQL function, used by every RLS policy
- predicate: firm_wide_visible OR owning_office matches user's office
  OR auth.uid() ∈ collaborators OR user is admin
- mirrored at app layer in Phase B (defense in depth)

RLS (real, not permissive):
- akten: visibility predicate; insert restricted to own office or admin;
  delete restricted to partners + admins
- parteien/fristen/dokumente/akten_events: inherit via can_see_akte(akte_id)
- termine: personal (akte_id NULL) visible only to creator; Akte-linked
  follow visibility predicate
- notizen: paliad.notiz_is_visible() resolves polymorphic parent
- reference tables: SELECT for any authenticated user
- users: SELECT all; UPDATE/INSERT only self
- feedback tables: INSERT for any authenticated user (write-only)

Seed data (ported from KanzlAI seed_upc_timeline.sql):
- 7 proceeding_types (INF, REV, CCR, APM, APP, AMD, ZPO_CIVIL)
- 40 deadline_rules (32 UPC + 4 ZPO + 4 cross-type appeal spawns)
  including conditional logic: Reply rule code (RoP.029b → 029a) and
  Rejoinder duration (1mo → 2mo) flip when CCR active
- 55 holidays (DE federal 2026/2027 + UPC summer 2026 + UPC winter 26/27)

Indexes per audit §3.3 + visibility-predicate hot paths:
- akten: (status, owning_office), (owning_office), partial on
  firm_wide_visible, GIN on collaborators
- fristen: (status, due_date), (akte_id)
- termine: (start_at), (akte_id)
- akten_events: (akte_id, created_at DESC)
- notizen: 4 partial indexes per parent type
- users: (office), (role)

Migration tooling:
- golang-migrate/migrate/v4 with embed.FS source
- Migrations live in internal/db/migrations/ (Go embed can't reach
  outside the package; this is the conventional Go layout for embedded
  migrations)
- Applied at server startup before HTTP listener binds
- DATABASE_URL is optional today (existing knowledge tools work without
  DB); becomes required once Phase B services land
- Mock Supabase auth schema for local testing in
  internal/db/migrations/_dev/mock_supabase_auth.sql (excluded from
  embed pattern by the underscore prefix)

Other changes:
- Dockerfile: bump golang to 1.24, copy go.sum (audit §2.9), rename
  binary patholo → paliad
- docker-compose.yml: add DATABASE_URL passthrough
- README.md: rewritten to reflect Paliad brand + Phase A migration system

Verified locally:
- 11 migrations applied cleanly against postgres:16-alpine
- RLS enabled on all 15 paliad.* tables (verified via pg_class.relrowsecurity)
- Visibility predicate verified with 4-case scenario:
  - Alice (Munich associate): sees Munich + firm-wide + collab-on (t f t t)
  - Bob (Düsseldorf associate): sees Düsseldorf + firm-wide + collab-on (f t t t)
  - Carol (Munich partner): sees Munich + firm-wide only (t f t f)
  - Anonymous: sees firm-wide only (f f t f)
- migrate down + re-up cycle clean (initial 007 down had ordering bug,
  fixed: drop policies before referenced function)
- Existing endpoints (/, /login) return 302 + 200 — no regressions
2026-04-16 13:54:19 +02:00

343 lines
24 KiB
SQL

-- Phase A seed: 32 UPC + 4 ZPO civil deadline rules.
-- Ported from /home/m/dev/KanzlAI/backend/seed/seed_upc_timeline.sql,
-- adapted to the paliad schema (no tenant_id, table paliad.deadline_rules).
DO $$
DECLARE
-- Proceeding-type FKs
v_inf integer;
v_rev integer;
v_ccr integer;
v_apm integer;
v_app integer;
v_amd integer;
v_zpo integer;
-- INF event IDs
v_inf_soc uuid;
v_inf_prelim uuid;
v_inf_sod uuid;
v_inf_reply uuid;
v_inf_rejoin uuid;
v_inf_interim uuid;
v_inf_oral uuid;
v_inf_decision uuid;
-- CCR event IDs
v_ccr_root uuid;
v_ccr_defence uuid;
v_ccr_reply uuid;
v_ccr_rejoin uuid;
v_ccr_interim uuid;
v_ccr_oral uuid;
v_ccr_decision uuid;
-- REV event IDs
v_rev_app uuid;
v_rev_defence uuid;
v_rev_reply uuid;
v_rev_rejoin uuid;
v_rev_interim uuid;
v_rev_oral uuid;
v_rev_decision uuid;
-- PI event IDs
v_pi_app uuid;
v_pi_resp uuid;
v_pi_oral uuid;
-- APP event IDs
v_app_notice uuid;
v_app_grounds uuid;
v_app_response uuid;
v_app_oral uuid;
BEGIN
SELECT id INTO v_inf FROM paliad.proceeding_types WHERE code = 'INF';
SELECT id INTO v_rev FROM paliad.proceeding_types WHERE code = 'REV';
SELECT id INTO v_ccr FROM paliad.proceeding_types WHERE code = 'CCR';
SELECT id INTO v_apm FROM paliad.proceeding_types WHERE code = 'APM';
SELECT id INTO v_app FROM paliad.proceeding_types WHERE code = 'APP';
SELECT id INTO v_amd FROM paliad.proceeding_types WHERE code = 'AMD';
SELECT id INTO v_zpo FROM paliad.proceeding_types WHERE code = 'ZPO_CIVIL';
-- ========================================================================
-- INFRINGEMENT (INF) — 9 rules
-- ========================================================================
v_inf_soc := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_soc, v_inf, NULL, 'inf.soc', 'Statement of Claim',
'Claimant files the statement of claim with the Registry',
'claimant', 'filing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_inf_prelim := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_prelim, v_inf, v_inf_soc, 'inf.prelim', 'Preliminary Objection',
'Defendant raises preliminary objection (jurisdiction, admissibility)',
'defendant', 'filing', false, 1, 'months', 'R.19',
'Rarely triggers separate decision; usually decided with main case',
false, NULL, 1, true);
v_inf_sod := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_sod, v_inf, v_inf_soc, 'inf.sod', 'Statement of Defence',
'Defendant files the statement of defence',
'defendant', 'filing', true, 3, 'months', 'RoP.023', NULL, false, NULL, 2, true);
v_inf_reply := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_reply, v_inf, v_inf_sod, 'inf.reply', 'Reply to Defence',
'Claimant''s reply to the statement of defence (includes Defence to Counterclaim if CCR active)',
'claimant', 'filing', true, 2, 'months', 'RoP.029b', NULL, false, NULL, 1, true);
v_inf_rejoin := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_rejoin, v_inf, v_inf_reply, 'inf.rejoin', 'Rejoinder',
'Defendant''s rejoinder to the reply',
'defendant', 'filing', true, 1, 'months', 'RoP.029c', NULL, false, NULL, 0, true);
v_inf_interim := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_interim, v_inf, v_inf_rejoin, 'inf.interim', 'Interim Conference',
'Interim conference with the judge-rapporteur',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_inf_oral := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_oral, v_inf, v_inf_interim, 'inf.oral', 'Oral Hearing',
'Oral hearing before the panel',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_inf_decision := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_inf_decision, v_inf, v_inf_oral, 'inf.decision', 'Decision',
'Panel delivers its decision',
'court', 'decision', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_app, v_inf_decision, 'inf.appeal', 'Appeal',
'Appeal against infringement decision to Court of Appeal',
'both', 'filing', true, 2, 'months', 'RoP.220.1', NULL, true, 'Appeal filed', 0, true);
-- ========================================================================
-- COUNTERCLAIM FOR REVOCATION (CCR) — 8 rules
-- ========================================================================
v_ccr_root := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_root, v_ccr, v_inf_sod, 'ccr.counterclaim', 'Counterclaim for Revocation',
'Defendant files counterclaim challenging patent validity (included in SoD)',
'defendant', 'filing', true, 0, 'months', NULL, NULL, true, 'Includes counterclaim for revocation', 0, true);
v_ccr_defence := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_defence, v_ccr, v_ccr_root, 'ccr.defence', 'Defence to Counterclaim',
'Patent proprietor files defence to revocation counterclaim',
'claimant', 'filing', true, 3, 'months', 'RoP.050', NULL, false, NULL, 0, true);
v_ccr_reply := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_reply, v_ccr, v_ccr_defence, 'ccr.reply', 'Reply in CCR',
'Reply in the counterclaim for revocation',
'defendant', 'filing', true, 2, 'months', NULL,
'Timing overlaps with infringement Rejoinder', false, NULL, 1, true);
v_ccr_rejoin := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_rejoin, v_ccr, v_ccr_reply, 'ccr.rejoin', 'Rejoinder in CCR',
'Rejoinder in the counterclaim for revocation',
'claimant', 'filing', true, 2, 'months', NULL, NULL, false, NULL, 0, true);
v_ccr_interim := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_interim, v_ccr, v_ccr_rejoin, 'ccr.interim', 'Interim Conference',
'Interim conference covering revocation issues',
'court', 'hearing', true, 0, 'months', NULL,
'May be combined with infringement IC', false, NULL, 0, true);
v_ccr_oral := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_oral, v_ccr, v_ccr_interim, 'ccr.oral', 'Oral Hearing',
'Oral hearing on validity',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_ccr_decision := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_ccr_decision, v_ccr, v_ccr_oral, 'ccr.decision', 'Decision',
'Decision on validity of the patent',
'court', 'decision', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_app, v_ccr_decision, 'ccr.appeal', 'Appeal',
'Appeal against revocation decision to Court of Appeal',
'both', 'filing', true, 2, 'months', 'RoP.220.1', NULL, true, 'Appeal filed', 0, true);
-- AMD spawn from CCR Defence
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_amd, v_ccr_defence, 'ccr.amend', 'Application to Amend Patent',
'Patent proprietor applies to amend the patent during revocation proceedings',
'claimant', 'filing', false, 0, 'months', NULL, NULL, true, 'Includes application to amend patent', 2, true);
-- ========================================================================
-- STANDALONE REVOCATION (REV) — 7 rules
-- ========================================================================
v_rev_app := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_app, v_rev, NULL, 'rev.app', 'Application for Revocation',
'Applicant files standalone application for revocation of the patent',
'claimant', 'filing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_rev_defence := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_defence, v_rev, v_rev_app, 'rev.defence', 'Defence to Revocation',
'Patent proprietor files defence to revocation application',
'defendant', 'filing', true, 3, 'months', NULL, NULL, false, NULL, 0, true);
v_rev_reply := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_reply, v_rev, v_rev_defence, 'rev.reply', 'Reply',
'Reply in standalone revocation proceedings',
'claimant', 'filing', true, 2, 'months', NULL, NULL, false, NULL, 1, true);
v_rev_rejoin := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_rejoin, v_rev, v_rev_reply, 'rev.rejoin', 'Rejoinder',
'Rejoinder in standalone revocation proceedings',
'defendant', 'filing', true, 2, 'months', NULL, NULL, false, NULL, 0, true);
v_rev_interim := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_interim, v_rev, v_rev_rejoin, 'rev.interim', 'Interim Conference',
'Interim conference with the judge-rapporteur',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_rev_oral := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_oral, v_rev, v_rev_interim, 'rev.oral', 'Oral Hearing',
'Oral hearing on validity in standalone revocation',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_rev_decision := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_rev_decision, v_rev, v_rev_oral, 'rev.decision', 'Decision',
'Decision on patent validity',
'court', 'decision', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_app, v_rev_decision, 'rev.appeal', 'Appeal',
'Appeal against revocation decision to Court of Appeal',
'both', 'filing', true, 2, 'months', 'RoP.220.1', NULL, true, 'Appeal filed', 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_amd, v_rev_defence, 'rev.amend', 'Application to Amend Patent',
'Patent proprietor applies to amend the patent',
'claimant', 'filing', false, 0, 'months', NULL, NULL, true, 'Includes application to amend patent', 2, true);
-- ========================================================================
-- PROVISIONAL MEASURES (APM) — 4 rules
-- ========================================================================
v_pi_app := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_pi_app, v_apm, NULL, 'pi.app', 'Application for Provisional Measures',
'Claimant files application for preliminary injunction',
'claimant', 'filing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_pi_resp := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_pi_resp, v_apm, v_pi_app, 'pi.response', 'Response to PI Application',
'Defendant files response to preliminary injunction application',
'defendant', 'filing', true, 0, 'months', NULL,
'Deadline set by court', false, NULL, 0, true);
v_pi_oral := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_pi_oral, v_apm, v_pi_resp, 'pi.oral', 'Oral Hearing',
'Oral hearing on provisional measures',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_apm, v_pi_oral, 'pi.order', 'Order on Provisional Measures',
'Court issues order on preliminary injunction',
'court', 'decision', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
-- ========================================================================
-- APPEAL standalone (APP) — 5 rules
-- ========================================================================
v_app_notice := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_app_notice, v_app, NULL, 'app.notice', 'Notice of Appeal',
'Appellant files notice of appeal with the Court of Appeal',
'both', 'filing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
v_app_grounds := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_app_grounds, v_app, v_app_notice, 'app.grounds', 'Statement of Grounds of Appeal',
'Appellant files statement of grounds',
'both', 'filing', true, 2, 'months', 'RoP.220.1', NULL, false, NULL, 0, true);
v_app_response := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_app_response, v_app, v_app_grounds, 'app.response', 'Response to Appeal',
'Respondent files response to the appeal',
'both', 'filing', true, 2, 'months', NULL, NULL, false, NULL, 0, true);
v_app_oral := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (v_app_oral, v_app, v_app_response, 'app.oral', 'Oral Hearing',
'Oral hearing before the Court of Appeal',
'court', 'hearing', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_app, v_app_oral, 'app.decision', 'Decision',
'Court of Appeal delivers its decision',
'court', 'decision', true, 0, 'months', NULL, NULL, false, NULL, 0, true);
-- ========================================================================
-- ZPO Civil (ZPO_CIVIL) — 4 rules (LG first-instance skeleton)
-- ========================================================================
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_zpo, NULL, 'zpo.klage', 'Klageschrift',
'Kläger reicht Klageschrift ein',
'claimant', 'filing', true, 0, 'months', '§ 253 ZPO', NULL, false, NULL, 0, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_zpo, NULL, 'zpo.vertanz', 'Verteidigungsanzeige',
'Beklagter zeigt Verteidigungsabsicht an (2 Wochen nach Zustellung)',
'defendant', 'filing', true, 2, 'weeks', '§ 276 Abs. 1 S. 1 ZPO',
'Notfrist ab Zustellung der Klageschrift', false, NULL, 1, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_zpo, NULL, 'zpo.klageerw', 'Klageerwiderung',
'Beklagter reicht Klageerwiderung ein (mindestens 2 Wochen)',
'defendant', 'filing', true, 2, 'weeks', '§ 276 Abs. 1 S. 2 ZPO',
'Vom Gericht gesetzt, mindestens 2 Wochen', false, NULL, 2, true);
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, is_spawn, spawn_label, sequence_order, is_active)
VALUES (gen_random_uuid(), v_zpo, NULL, 'zpo.berufung', 'Berufung',
'Berufung gegen erstinstanzliches Urteil (1 Monat ab Zustellung)',
'both', 'filing', true, 1, 'months', '§ 517 ZPO',
'Notfrist ab Zustellung des vollständigen Urteils', false, NULL, 3, true);
-- ========================================================================
-- Conditional deadlines (from KanzlAI migration 040):
-- Reply to Defence: rule code changes when CCR active (029b → 029a)
-- Rejoinder: duration changes when CCR active (1 month → 2 months; 029c → 029d)
-- ========================================================================
UPDATE paliad.deadline_rules
SET condition_rule_id = v_ccr_root,
alt_rule_code = 'RoP.029a'
WHERE id = v_inf_reply;
UPDATE paliad.deadline_rules
SET condition_rule_id = v_ccr_root,
alt_duration_value = 2,
alt_duration_unit = 'months',
alt_rule_code = 'RoP.029d'
WHERE id = v_inf_rejoin;
END $$;