Merge: t-paliad-361 caption-wording — Respondent + restored Streitpatent line + DE appeal/nullity role-label backfill (mig 163, lexy-confirmed)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled

This commit is contained in:
mAi
2026-06-01 15:23:09 +02:00
3 changed files with 269 additions and 22 deletions

View File

@@ -0,0 +1,40 @@
-- Revert 163_caption_wording_followup (t-paliad-361). Restores the A-S2
-- (post-mig-161 / mig-137) state for all three changes.
-- ----------------------------------------------------------------
-- Change 1 down — UPC appeal EN responding party back to 'Appellee'.
-- ----------------------------------------------------------------
UPDATE paliad.proceeding_types
SET role_reactive_label_en = 'Appellee'
WHERE code = 'upc.apl.unified'
AND role_reactive_label_en = 'Respondent';
-- ----------------------------------------------------------------
-- Change 2 down — drop the Streitpatent line from the upc-formal caption seed,
-- restoring the verbatim post-mig-161 parametric seed.
-- ----------------------------------------------------------------
UPDATE paliad.submission_bases AS b
SET section_spec = jsonb_set(b.section_spec, '{defaults}', (
SELECT jsonb_agg(
CASE WHEN elem->>'section_key' = 'caption'
THEN elem || jsonb_build_object(
'seed_md_de', E'{{caption.heading_de}}\n\n**{{parties.claimant.0.name}}**\nvertreten durch {{parties.claimant.0.representative}}\n\n— {{caption.claimant_designation_de}} —\n\n{{caption.versus_de}}\n\n**{{parties.defendant.0.name}}**\nvertreten durch {{parties.defendant.0.representative}}\n\n— {{caption.defendant_designation_de}} —\n\nwegen {{caption.subject_de}}\n\nAktenzeichen: {{project.case_number}}\n{{project.court}}',
'seed_md_en', E'{{caption.heading_en}}\n\n**{{parties.claimant.0.name}}**\nrepresented by {{parties.claimant.0.representative}}\n\n— {{caption.claimant_designation_en}} —\n\n{{caption.versus_en}}\n\n**{{parties.defendant.0.name}}**\nrepresented by {{parties.defendant.0.representative}}\n\n— {{caption.defendant_designation_en}} —\n\nre {{caption.subject_en}}\n\nCase number: {{project.case_number}}\n{{project.court}}')
ELSE elem END
ORDER BY ord)
FROM jsonb_array_elements(b.section_spec->'defaults') WITH ORDINALITY AS d(elem, ord)))
WHERE b.slug = 'upc-formal' AND b.section_spec ? 'defaults';
-- ----------------------------------------------------------------
-- Change 3 down — clear the backfilled role labels (back to NULL, the
-- pre-163 state for these four proceedings).
-- ----------------------------------------------------------------
UPDATE paliad.proceeding_types
SET role_proactive_label_de = NULL,
role_reactive_label_de = NULL,
role_proactive_label_en = NULL,
role_reactive_label_en = NULL
WHERE code IN ('de.inf.olg', 'de.inf.bgh', 'de.null.bpatg', 'de.null.bgh');

View File

@@ -0,0 +1,108 @@
-- 163_caption_wording_followup — t-paliad-361, follow-up to t-paliad-358 A-S2.
--
-- m ruled on the 7 lexy-wording flags from A-S2 via AskUserQuestion
-- (2026-06-01 14:30). Most flags CONFIRMED the live wording; three changes
-- land here. All three are caption (Rubrum) wording and share this one
-- reversible migration.
--
-- Change 1 — UPC appeal responding party (EN): 'Appellee' → 'Respondent'.
-- m chose Respondent over Appellee. The only place 'Appellee' is stored is
-- the mig-137 role-label override on upc.apl.unified (id=160, retired by
-- mig 155 but kept as the canonical UPC-appeal role-label row). The caption
-- resolver's instance-derived EN fallback already says 'Respondent'
-- (submission_vars.go), so this fixes the wording at the data source rather
-- than downstream. DE side (Berufungsbeklagter) is left untouched per m.
--
-- Change 2 — restore the standalone 'Streitpatent' / 'Patent in suit' line in
-- the upc-formal Composer caption seed. A-S2 (mig 161) dropped it when it
-- unified the caption onto the {{caption.*}} keys. m wants the patent-in-suit
-- line back, but KEEPS the parametric 'In der Sache' heading (he did not
-- revert that). Only the upc-formal base's caption seed is touched.
--
-- Change 3 — backfill role-label overrides for the four DE appeal/nullity
-- proceedings that carry none (de.inf.olg, de.inf.bgh, de.null.bpatg,
-- de.null.bgh). Without an override these fall to the instance-derived path,
-- which is only correct when project.instance_level is set. The backfill
-- makes the designations right regardless of instance_level. Wording is
-- lexy-confirmed (statute-grounded: §§ 511, 542, 544 ZPO; §§ 81, 110 PatG),
-- bracketed-inclusive gender style to match the A-S2-confirmed convention.
--
-- ADDITIVE / data-only. No schema changes. Reversible (see .down.sql).
-- ----------------------------------------------------------------
-- Change 1 — UPC appeal EN responding party: 'Appellee' → 'Respondent'.
-- ----------------------------------------------------------------
UPDATE paliad.proceeding_types
SET role_reactive_label_en = 'Respondent'
WHERE code = 'upc.apl.unified'
AND role_reactive_label_en = 'Appellee';
-- ----------------------------------------------------------------
-- Change 2 — restore the Streitpatent line in the upc-formal caption seed.
-- Position-independent: rewrites only the section_key='caption' element of
-- section_spec->'defaults', preserving order (WITH ORDINALITY) and every
-- other field on the element (elem || patch). Keeps the parametric heading;
-- re-adds 'Streitpatent: {{project.patent_number_upc}}' (DE) /
-- 'Patent in suit: {{...}}' (EN) grouped with the case number, ahead of the
-- {{project.court}} line.
-- ----------------------------------------------------------------
UPDATE paliad.submission_bases AS b
SET section_spec = jsonb_set(
b.section_spec,
'{defaults}',
(
SELECT jsonb_agg(
CASE WHEN elem->>'section_key' = 'caption'
THEN elem || jsonb_build_object(
'seed_md_de', E'{{caption.heading_de}}\n\n**{{parties.claimant.0.name}}**\nvertreten durch {{parties.claimant.0.representative}}\n\n— {{caption.claimant_designation_de}} —\n\n{{caption.versus_de}}\n\n**{{parties.defendant.0.name}}**\nvertreten durch {{parties.defendant.0.representative}}\n\n— {{caption.defendant_designation_de}} —\n\nwegen {{caption.subject_de}}\n\nAktenzeichen: {{project.case_number}}\nStreitpatent: {{project.patent_number_upc}}\n{{project.court}}',
'seed_md_en', E'{{caption.heading_en}}\n\n**{{parties.claimant.0.name}}**\nrepresented by {{parties.claimant.0.representative}}\n\n— {{caption.claimant_designation_en}} —\n\n{{caption.versus_en}}\n\n**{{parties.defendant.0.name}}**\nrepresented by {{parties.defendant.0.representative}}\n\n— {{caption.defendant_designation_en}} —\n\nre {{caption.subject_en}}\n\nCase number: {{project.case_number}}\nPatent in suit: {{project.patent_number_upc}}\n{{project.court}}')
ELSE elem
END
ORDER BY ord
)
FROM jsonb_array_elements(b.section_spec->'defaults') WITH ORDINALITY AS d(elem, ord)
)
)
WHERE b.slug = 'upc-formal'
AND b.section_spec ? 'defaults';
-- ----------------------------------------------------------------
-- Change 3 — backfill lexy-confirmed role labels for the four DE
-- appeal/nullity proceedings (mig-137 mechanism). Bracketed-inclusive
-- gender style; EN equivalents.
--
-- de.inf.olg Berufungskläger(in) / Berufungsbeklagte(r) // Appellant / Respondent (§ 511 ZPO Berufung)
-- de.inf.bgh Revisionskläger(in) / Revisionsbeklagte(r) // Appellant / Respondent (§§ 542/544 ZPO; Revision as default over NZB)
-- de.null.bpatg Nichtigkeitskläger(in) / Beklagte(r) (Patentinhaber(in)) // Nullity claimant / Defendant (patent proprietor) (§ 81 PatG)
-- de.null.bgh Berufungskläger(in) / Berufungsbeklagte(r) // Appellant / Respondent (§ 110 PatG, post-2009 Berufung)
-- ----------------------------------------------------------------
UPDATE paliad.proceeding_types
SET role_proactive_label_de = 'Berufungskläger(in)',
role_reactive_label_de = 'Berufungsbeklagte(r)',
role_proactive_label_en = 'Appellant',
role_reactive_label_en = 'Respondent'
WHERE code = 'de.inf.olg';
UPDATE paliad.proceeding_types
SET role_proactive_label_de = 'Revisionskläger(in)',
role_reactive_label_de = 'Revisionsbeklagte(r)',
role_proactive_label_en = 'Appellant',
role_reactive_label_en = 'Respondent'
WHERE code = 'de.inf.bgh';
UPDATE paliad.proceeding_types
SET role_proactive_label_de = 'Nichtigkeitskläger(in)',
role_reactive_label_de = 'Beklagte(r) (Patentinhaber(in))',
role_proactive_label_en = 'Nullity claimant',
role_reactive_label_en = 'Defendant (patent proprietor)'
WHERE code = 'de.null.bpatg';
UPDATE paliad.proceeding_types
SET role_proactive_label_de = 'Berufungskläger(in)',
role_reactive_label_de = 'Berufungsbeklagte(r)',
role_proactive_label_en = 'Appellant',
role_reactive_label_en = 'Respondent'
WHERE code = 'de.null.bgh';

View File

@@ -1,9 +1,18 @@
package services
// Pins the parametric caption resolver (t-paliad-358 A-S2): heading / subject
// derive from jurisdiction + the proceeding code's nature segment; designations
// reuse the proceeding role-label overrides, fall back to instance-derived
// appeal/cassation wording, then to the civil default.
// Pins the parametric caption resolver (t-paliad-358 A-S2 + t-paliad-361
// wording follow-up): heading / subject derive from jurisdiction + the
// proceeding code's nature segment; designations reuse the proceeding
// role-label overrides, fall back to instance-derived appeal/cassation
// wording, then to the civil default.
//
// t-paliad-361 additions:
// - UPC appeal EN responding party is now "Respondent" (not "Appellee").
// - The four DE appeal/nullity proceedings (de.inf.olg, de.inf.bgh,
// de.null.bpatg, de.null.bgh) carry lexy-confirmed role-label overrides
// (mig 163), so their designations are correct even when
// project.instance_level is unset — pinned by the cases below that pass an
// empty Project but still expect the appeal/nullity wording.
import (
"testing"
@@ -17,6 +26,18 @@ func ptType(code, jurisdiction string) *models.ProceedingType {
return &models.ProceedingType{Code: code, Jurisdiction: sp(jurisdiction)}
}
// ptRoles builds a proceeding type carrying the mig-137/mig-163 role-label
// overrides (the four-column bracketed-inclusive designations).
func ptRoles(code, jurisdiction, proDE, reDE, proEN, reEN string) *models.ProceedingType {
return &models.ProceedingType{
Code: code, Jurisdiction: sp(jurisdiction),
RoleProactiveLabelDE: sp(proDE),
RoleReactiveLabelDE: sp(reDE),
RoleProactiveLabelEN: sp(proEN),
RoleReactiveLabelEN: sp(reEN),
}
}
func TestResolveCaption(t *testing.T) {
cases := []struct {
name string
@@ -26,6 +47,8 @@ func TestResolveCaption(t *testing.T) {
wantClaimDE string
wantDefDE string
wantSubjDE string
wantClaimEN string
wantDefEN string
}{
{
name: "DE LG infringement → Rechtsstreit / Kläger-Beklagte / Patentverletzung",
@@ -35,15 +58,19 @@ func TestResolveCaption(t *testing.T) {
wantClaimDE: "Klägerin",
wantDefDE: "Beklagte",
wantSubjDE: "Patentverletzung",
wantClaimEN: "Claimant",
wantDefEN: "Defendant",
},
{
name: "DE BPatG nullity → Patentnichtigkeitssache",
name: "DE BPatG nullity (no role-label data) → civil default fallback",
project: &models.Project{},
pt: ptType("de.null.bpatg", "DE"),
wantHeadDE: "In der Patentnichtigkeitssache",
wantClaimDE: "Klägerin",
wantDefDE: "Beklagte",
wantSubjDE: "Nichtigkeit des Streitpatents",
wantClaimEN: "Claimant",
wantDefEN: "Defendant",
},
{
name: "UPC infringement → In der Sache / civil default",
@@ -53,54 +80,120 @@ func TestResolveCaption(t *testing.T) {
wantClaimDE: "Klägerin",
wantDefDE: "Beklagte",
wantSubjDE: "Patentverletzung",
wantClaimEN: "Claimant",
wantDefEN: "Defendant",
},
{
name: "UPC revocation → role-label override (Antragsteller Nichtigkeit)",
project: &models.Project{},
pt: &models.ProceedingType{
Code: "upc.rev.cfi", Jurisdiction: sp("UPC"),
RoleProactiveLabelDE: sp("Antragsteller (Nichtigkeit)"),
RoleReactiveLabelDE: sp("Antragsgegner (Nichtigkeit)"),
},
pt: ptRoles("upc.rev.cfi", "UPC",
"Antragsteller (Nichtigkeit)", "Antragsgegner (Nichtigkeit)",
"Revocation claimant", "Revocation defendant"),
wantHeadDE: "In der Sache",
wantClaimDE: "Antragsteller (Nichtigkeit)",
wantDefDE: "Antragsgegner (Nichtigkeit)",
wantSubjDE: "Nichtigkeit des Streitpatents",
wantClaimEN: "Revocation claimant",
wantDefEN: "Revocation defendant",
},
{
name: "UPC appeal → role-label override wins over instance",
// t-paliad-361 Change 1: the role-label override wins over the
// instance-derived path, and its EN reactive label is now
// "Respondent" (was "Appellee", mig 163).
name: "UPC appeal → role-label override wins, EN reactive is Respondent",
project: &models.Project{InstanceLevel: sp("appeal")},
pt: &models.ProceedingType{
Code: "upc.apl.unified", Jurisdiction: sp("UPC"),
RoleProactiveLabelDE: sp("Berufungskläger"),
RoleReactiveLabelDE: sp("Berufungsbeklagter"),
},
pt: ptRoles("upc.apl.unified", "UPC",
"Berufungskläger", "Berufungsbeklagter",
"Appellant", "Respondent"),
wantHeadDE: "In der Sache",
wantClaimDE: "Berufungskläger",
wantDefDE: "Berufungsbeklagter",
wantSubjDE: "Patentstreitsache",
wantClaimEN: "Appellant",
wantDefEN: "Respondent",
},
{
name: "DE OLG appeal via instance_level (no role-label data)",
name: "DE OLG appeal via instance_level (no role-label data) → instance-derived",
project: &models.Project{InstanceLevel: sp("appeal")},
pt: ptType("de.inf.olg", "DE"),
wantHeadDE: "In dem Rechtsstreit",
wantClaimDE: "Berufungskläger(in)",
wantDefDE: "Berufungsbeklagte(r)",
wantSubjDE: "Patentverletzung",
wantClaimEN: "Appellant",
wantDefEN: "Respondent",
},
{
name: "EPA opposition → Einsprechende(r) / Patentinhaber(in)",
project: &models.Project{},
pt: &models.ProceedingType{
Code: "epa.opp.opd", Jurisdiction: sp("EPA"),
RoleProactiveLabelDE: sp("Einsprechende(r)"),
RoleReactiveLabelDE: sp("Patentinhaber(in)"),
},
pt: ptRoles("epa.opp.opd", "EPA",
"Einsprechende(r)", "Patentinhaber(in)",
"Opponent", "Patentee"),
wantHeadDE: "Im Einspruchsverfahren",
wantClaimDE: "Einsprechende(r)",
wantDefDE: "Patentinhaber(in)",
wantSubjDE: "Einspruch gegen das Streitpatent",
wantClaimEN: "Opponent",
wantDefEN: "Patentee",
},
// ---------------------------------------------------------------
// t-paliad-361 Change 3: the four DE appeal/nullity proceedings now
// carry lexy-confirmed role-label overrides (mig 163). Each case
// passes an EMPTY Project (instance_level unset) to prove the override
// yields the correct designation without relying on the instance path.
// ---------------------------------------------------------------
{
name: "de.inf.olg backfill → Berufungskläger(in)/Berufungsbeklagte(r), instance unset",
project: &models.Project{},
pt: ptRoles("de.inf.olg", "DE",
"Berufungskläger(in)", "Berufungsbeklagte(r)",
"Appellant", "Respondent"),
wantHeadDE: "In dem Rechtsstreit",
wantClaimDE: "Berufungskläger(in)",
wantDefDE: "Berufungsbeklagte(r)",
wantSubjDE: "Patentverletzung",
wantClaimEN: "Appellant",
wantDefEN: "Respondent",
},
{
name: "de.inf.bgh backfill → Revisionskläger(in)/Revisionsbeklagte(r), instance unset",
project: &models.Project{},
pt: ptRoles("de.inf.bgh", "DE",
"Revisionskläger(in)", "Revisionsbeklagte(r)",
"Appellant", "Respondent"),
wantHeadDE: "In dem Rechtsstreit",
wantClaimDE: "Revisionskläger(in)",
wantDefDE: "Revisionsbeklagte(r)",
wantSubjDE: "Patentverletzung",
wantClaimEN: "Appellant",
wantDefEN: "Respondent",
},
{
name: "de.null.bpatg backfill → Nichtigkeitskläger(in)/Beklagte(r) (Patentinhaber(in))",
project: &models.Project{},
pt: ptRoles("de.null.bpatg", "DE",
"Nichtigkeitskläger(in)", "Beklagte(r) (Patentinhaber(in))",
"Nullity claimant", "Defendant (patent proprietor)"),
wantHeadDE: "In der Patentnichtigkeitssache",
wantClaimDE: "Nichtigkeitskläger(in)",
wantDefDE: "Beklagte(r) (Patentinhaber(in))",
wantSubjDE: "Nichtigkeit des Streitpatents",
wantClaimEN: "Nullity claimant",
wantDefEN: "Defendant (patent proprietor)",
},
{
name: "de.null.bgh backfill → Berufungskläger(in)/Berufungsbeklagte(r) (§110 PatG Berufung)",
project: &models.Project{},
pt: ptRoles("de.null.bgh", "DE",
"Berufungskläger(in)", "Berufungsbeklagte(r)",
"Appellant", "Respondent"),
wantHeadDE: "In der Patentnichtigkeitssache",
wantClaimDE: "Berufungskläger(in)",
wantDefDE: "Berufungsbeklagte(r)",
wantSubjDE: "Nichtigkeit des Streitpatents",
wantClaimEN: "Appellant",
wantDefEN: "Respondent",
},
}
@@ -119,6 +212,12 @@ func TestResolveCaption(t *testing.T) {
if got.subjectDE != c.wantSubjDE {
t.Errorf("subjectDE = %q, want %q", got.subjectDE, c.wantSubjDE)
}
if got.claimantEN != c.wantClaimEN {
t.Errorf("claimantEN = %q, want %q", got.claimantEN, c.wantClaimEN)
}
if got.defendantEN != c.wantDefEN {
t.Errorf("defendantEN = %q, want %q", got.defendantEN, c.wantDefEN)
}
})
}
}