Files
paliad/internal/db/migrations/161_composer_caption_parametric.up.sql
mAi b99b6d6fb5 feat(submissions): parametric caption.* keys — unify Rubrum across all render paths (t-paliad-358 A-S2)
Promotes the case caption (Rubrum) to ONE parametric set of resolver keys
(caption.*) consumed identically by every render path, so the wording no
longer diverges per path and reflects the forum.

New caption.* keys (addCaptionVars, submission_vars.go), each in bare +
_de + _en forms (bare resolves to the draft language):
  caption.heading · caption.claimant_designation · caption.defendant_designation
  caption.versus · caption.subject

Parametrised from data already in the bag — NO new schema:
  - designations reuse the proceeding-type role-label overrides (mig 137:
    upc.apl.unified→Berufungskläger, upc.rev.cfi→Antragsteller (Nichtigkeit),
    epa.opp.*→Einsprechende(r)/Patentinhaber(in)); else instance-derived
    appeal/cassation (project.instance_level); else civil default Klägerin/
    Beklagte // Claimant/Defendant.
  - heading + subject from jurisdiction + the dotted code's nature segment
    (inf→"In dem Rechtsstreit"/"Patentverletzung", null→"In der
    Patentnichtigkeitssache", UPC→"In der Sache"/"In the matter",
    opp→"Im Einspruchsverfahren").
Also exposes project.proceeding.jurisdiction.

All three render paths now reference the SAME keys:
  1. docx.BuildFallbackSkeleton (merge fallback) — heading/designations/versus/
     wegen-subject are {{caption.*}} placeholders.
  2. demo per-code template de.inf.lg.erwidg.docx (mWorkRepo) — regenerated via
     scripts/gen-demo-submission-template; caption wired to caption.*; closing
     drops the duplicate {{firm.name}} line (now carried by signature_block).
     Pushed to HL/mWorkRepo as mAi (commit 3682299).
  3. Composer caption seeds — mig 161 rewrites the caption section seed_md of
     all 4 bases (hlc-letterhead, neutral, lg-duesseldorf, upc-formal) to the
     parametric form (position-independent jsonb_agg patch; reversible down).

our_side is intentionally NOT a caption driver — the caption designates both
parties by procedural role regardless of which side we act for.

Tests: resolveCaption forum matrix (DE-LG/BPatG/UPC/role-label/instance-appeal/
EPA-opp), bare-resolves-to-lang, fallback skeleton renders caption.* keys.
mig 161 passes TestMigrations_DryRun. go vet ./... + bun build clean; all
touched packages green (the live approval/migration_136 failures are
pre-existing shared-DB env issues, unrelated).

LEXY-REVIEW FLAGS in the report — DE caption conventions are practitioner
convention, not in the youpc corpus; specific wordings flagged for sign-off.
2026-06-01 12:59:29 +02:00

44 lines
2.7 KiB
SQL

-- t-paliad-358 A-S2 — unify the Composer caption (Rubrum) seed across every
-- base onto the shared parametric caption.* resolver keys.
--
-- Before: each base seeded a hand-written caption with hard-coded designations
-- ("— Klägerin —" / "— Claimant —") and heading ("In der Sache" / "In the
-- matter"). That wording diverged from the per-code .docx templates and the
-- merge-fallback skeleton, and could not reflect the forum (UPC vs DE-LG vs
-- nullity vs appeal).
--
-- After: every base's caption section references the {{caption.*}} keys
-- (addCaptionVars, submission_vars.go), so the heading, party designations,
-- versus connector and "wegen" subject are resolved per forum from
-- project.proceeding (jurisdiction + code + role-label overrides) +
-- project.instance_level — the SAME wording the templates and the fallback
-- skeleton now use. One parametric caption, shared keys.
--
-- Forward-only effect: section seeds are applied when a NEW draft is created
-- from a base; existing drafts keep their already-seeded (possibly user-edited)
-- caption text untouched.
--
-- Position-independent: rewrites only the element whose section_key='caption'
-- inside section_spec->'defaults', preserving order (WITH ORDINALITY) and every
-- other field on the element (elem || patch).
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 IN ('hlc-letterhead', 'neutral', 'lg-duesseldorf', 'upc-formal')
AND b.section_spec ? 'defaults';