diff --git a/docs/design-proceeding-code-taxonomy-2026-05-18.md b/docs/design-proceeding-code-taxonomy-2026-05-18.md new file mode 100644 index 0000000..e255c9b --- /dev/null +++ b/docs/design-proceeding-code-taxonomy-2026-05-18.md @@ -0,0 +1,172 @@ +# Proceeding-code taxonomy (t-paliad-204 ratified 2026-05-18) + +> Source of truth for `paliad.proceeding_types.code`. Every active row's +> `code` MUST conform to the convention below. This document anchors +> migration 096 (`internal/db/migrations/096_proceeding_code_rename.up.sql`) +> and the post-migration determinator + fristenrechner mapping in +> `internal/services/proceeding_mapping.go`. + +## 0. Why we renamed + +The historical `code` strings (`UPC_INF`, `DE_INF`, `EPA_OPP`, …) were +UPPER_SNAKE jurisdiction-glued-to-acronym slugs. They were structurally +opaque and the taxonomy grew unevenly as more proceedings entered the +fristenrechner — `UPC_APP` covers all UPC appeals, `DE_INF_OLG` / +`DE_INF_BGH` carry the instance hint inline, `EP_GRANT` is the only EPA +row with no `EPA_` prefix at all. The mapping in +`internal/services/proceeding_mapping.go` had to special-case appeal +ambiguities (no instance hint on UPC_APP, none on the DE side either). +After mig 095 landed the t-paliad-205 fristen gap-fill, m and paliadin +ratified a uniform convention for the corpus, captured here. + +## 0.1 Convention + +Active proceeding codes are lowercase, dot-separated, three positions: + + .. + +* **``** — one of `upc`, `de`, `epa`, `dpma`. +* **`` / ``** — contextual; for first-instance proceedings they are + `.` (e.g. `de.inf.lg` for Verletzungsklage am + Landgericht). For appeals they are `.` (e.g. + `upc.apl.merits`, `upc.apl.cost`, `upc.apl.order`). +* The CHECK constraint installed by mig 096 enforces + `code ~ '^[a-z]+\.[a-z]+\.[a-z]+$'` on every active row, with a + carve-out for the legacy `_archived_litigation` bucket + (`code ~ '^_archived_'`). + +The convention is forward-looking: any new fristenrechner row added +after mig 096 MUST conform — no further UPPER_SNAKE codes. + +## 0.2 Ratified taxonomy + +### UPC + +| New code | Old code | id | Notes | +|--------------------|------------------|----|------------------------------------------------------------------------| +| `upc.inf.cfi` | `UPC_INF` | 8 | Verletzungsverfahren, CFI | +| `upc.rev.cfi` | `UPC_REV` | 9 | Nichtigkeitsverfahren, CFI | +| `upc.ccr.cfi` | _new_ | _new_ | Widerklage auf Nichtigkeit — illustrative peer of `upc.inf.cfi`. Rules live on `upc.inf.cfi` with `with_ccr=true`. See §1 sub-decision S1. | +| `upc.pi.cfi` | `UPC_PI` | 10 | Einstweilige Maßnahmen | +| `upc.dmgs.cfi` | `UPC_DAMAGES` | 17 | Schadensbemessung | +| `upc.disc.cfi` | `UPC_DISCOVERY` | 18 | Bucheinsicht | +| `upc.apl.merits` | `UPC_APP` | 11 | Hauptberufung — covers inf + rev + ccr + damages-merits appeals | +| `upc.apl.order` | `UPC_APP_ORDERS` | 20 | 15-Tage-Beschwerde gegen Anordnungen (R.220 (1)(c)) | +| `upc.apl.cost` | `UPC_COST_APPEAL`| 19 | Kostenbeschwerde | + +### DE + +| New code | Old code | id | Notes | +|---------------------|------------------------|----|-------------------------------------------------------------| +| `de.inf.lg` | `DE_INF` | 12 | Verletzungsklage am Landgericht | +| `de.inf.olg` | `DE_INF_OLG` | 25 | Berufung am OLG | +| `de.inf.bgh` | `DE_INF_BGH` | 26 | Revision + NZB merged — `with_nzb` flag on NZB-detour rules | +| `de.null.bpatg` | `DE_NULL` | 13 | Nichtigkeitsverfahren am BPatG | +| `de.null.bgh` | `DE_NULL_BGH` | 27 | Nichtigkeitsberufung am BGH | + +### EPA + +| New code | Old code | id | Notes | +|---------------------|--------------|----|------------------------------------------------| +| `epa.grant.exa` | `EP_GRANT` | 16 | EP-Erteilungsverfahren | +| `epa.opp.opd` | `EPA_OPP` | 14 | Einspruchsverfahren | +| `epa.opp.boa` | `EPA_APP` | 15 | Einspruchsbeschwerde (Board of Appeal) | + +### DPMA + +| New code | Old code | id | Notes | +|-----------------------|-------------------------|----|----------------------------------------------------------------| +| `dpma.opp.dpma` | `DPMA_OPP` | 28 | Einspruch beim DPMA | +| `dpma.appeal.bpatg` | `DPMA_BPATG_BESCHWERDE` | 29 | Beschwerde am BPatG (generic — source differentiated at rule level) | +| `dpma.appeal.bgh` | `DPMA_BGH_RB` | 30 | Rechtsbeschwerde am BGH (generic — source differentiated at rule level) | + +### Archived + +| Code | id | Notes | +|-------------------------|----|----------------------------------------| +| `_archived_litigation` | 32 | Unchanged — Pipeline-A retired corpus | + +IDs are stable. Only the `code` STRING changes. The FKs +`deadline_rules.proceeding_type_id`, `projects.proceeding_type_id`, and +`deadline_rules.spawn_proceeding_type_id` reference IDs, so the existing +rule corpus and spawn wiring (incl. mig 095's `spawn_proceeding_type_id=11`) +continue to work unchanged. + +## 0.3 Sub-decisions (m's calls, 2026-05-18) + +### S1 — `upc.ccr.cfi` visibility + +`is_active=true`, visible in the determinator + dropdowns. **No rules +attached.** When the determinator surfaces it, the UI shows the hint: + +> "Regeln liegen auf upc.inf.cfi (with_ccr=true); wir leiten Sie dorthin +> weiter." + +Routing logic lands in `internal/services/proceeding_mapping.go` — when +the cascade resolves to `upc.ccr.cfi`, the mapping returns the +`upc.inf.cfi` id (=8) with `with_ccr=true` as a default flag. The peer +exists for taxonomic completeness so users searching for +"Widerklage" find an entry; it is not a separate rule namespace. + +### S2 — Abbreviations + +`dmgs` for damages, `disc` for discovery. m's call: short form keeps the +codes terse and the dot-separated shape readable. + +### S3 — Damages appeal + +**NO separate code.** `upc.apl.merits` covers damages appeals — the +spawn rules from `upc.dmgs.cfi` (none seeded today) would carry their +own `spawn_label`. Avoids a code like `upc.apl.dmgs` whose rules would +be empty for the foreseeable future. + +### S4 — NZB at BGH + +Single bucket `de.inf.bgh`. Rules diverging in the NZB-detour-path +(Nichtzulassungsbeschwerde when the OLG didn't grant Revision) use a +`with_nzb` flag instead of a separate proceeding type. Keeps the dropdown +list shorter and matches how m practitioners think about the BGH +instance — same destination, two ways to arrive. + +### S5 — DPMA appeals + +Generic `dpma.appeal.bpatg` / `dpma.appeal.bgh` — source-of-decision +differentiation (was it a DPMA decision being appealed? a BPatG +decision being further appealed to BGH?) lives at the rule level, not +the proceeding-type level. Keeps the code namespace flat. + +## 0.4 Spawn-FK invariant + +After mig 096, the spawn FK invariant from mig 095 still holds: + + deadline_rules.spawn_proceeding_type_id = 11 + ↔ paliad.proceeding_types[id=11].code = 'upc.apl.merits' + +Spawn rules from `upc.inf.cfi` / `upc.rev.cfi` chain to the appeal-merits +proceeding without code-string awareness. Same for any future spawn FK. + +## 0.5 Not in scope + +* `paliad.event_categories.slug` segments (`upc-inf`, `de-bgh-null`, …) + are NOT renamed. They are stable identifiers in a separate taxonomy and + their kebab form is presentation-layer (it appears in URL fragments). + Mig 096 only updates the `proceeding_type_code` text column on + `paliad.event_category_concepts` rows so the soft join through + `event_category_concepts → proceeding_types.code` keeps resolving. +* Fee-table keys (`EPA_OPPOSITION`, `UPC_APPEAL`, …) in + `internal/calc/fees.go` are NOT proceeding codes — they are fee-table + bucket keys with their own naming. Untouched. +* Forum bucket slugs (`upc_cfi`, `de_lg`, …) in + `ForumToProceedingCodes` are presentation buckets, not codes. The + values inside (`UPC_INF`, …) are the codes being renamed. + +## 0.6 References + +* `internal/db/migrations/096_proceeding_code_rename.up.sql` — the + migration that lands this rename. +* `internal/services/proceeding_mapping.go` — post-mig 096 mapping with + the ccr-routing helper (S1). +* `internal/services/proceeding_codes_shape_test.go` — Go test asserting + every active fristenrechner-category code matches the new shape regex. +* mig 095 (`internal/db/migrations/095_fristen_gap_fill.up.sql`) — the + immediate predecessor; spawn_proceeding_type_id=11 carries through.