Sweeps internal/services + internal/handlers + internal/models to use
the new proceeding codes landed by mig 096. Stable Code* constants
live in internal/services/proceeding_mapping.go so a future rename
needs to touch one file.
Substantive changes:
- proceeding_mapping.go gains ResolveCounterclaimRouting() — the
cascade resolver that routes upc.ccr.cfi (illustrative peer) back
to upc.inf.cfi with with_ccr=true as default flag (design doc S1).
- deadline_search_service.go forum-bucket map updated; upc.ccr.cfi
added to upc_cfi since it is a CFI peer.
- project_service.go CreateCounterclaim default lookup parameterised
so the SQL string carries the constant, not a literal.
- proceeding_codes_shape_test.go: new file. Validates the shape
regex standalone (always runs) and walks live DB rows asserting
every active fristenrechner row matches the new shape + every
stable Code* constant resolves to exactly one active row.
Comments and test fixtures throughout the Go tree updated to the
new shape. Tests pass under `go test ./internal/... -short`.
Lorenz's Slice 9 (t-paliad-195) deferred mig 093 because 40 active
paliad.deadline_rules still pointed at the 7 litigation-category
proceeding_types (INF, REV, CCR, APM, APP, AMD, ZPO_CIVIL). Phase 3
Slice 5 (mig 087/088) already retired the category from project-binding;
this migration retires it from the rule corpus.
PLAN CHOICE (audit-gated, paliadin-approved): archive-all-40 rather than
the original re-parent plan. The audit found that 23 of 40 Pipeline-A
rules share their `code` with an existing fristenrechner rule on the
proposed re-parent target (e.g. inf.oral exists on both INF and
UPC_INF). Re-parenting would leave two rules with identical
(proceeding_type_id, code), breaking the implicit per-proceeding
rule_code identity contract keyed off by projection / search /
rule_editor. The fristenrechner rules are clearly the production
version (proper German names, legal_source pinned to UPC.RoP citations,
full bilateral chains, intra-proceeding counterclaim handling); the
Pipeline-A rules are stubs (English-only, mostly NULL legal_source,
duration_value=0 for 28 of 40, no spawn_proceeding_type_id wiring).
Migration 093 sequence (atomic):
1. Snapshot proceeding_types_pre_093 + deadline_rules_pre_093 as
permanent audit anchors.
2. INSERT _archived_litigation pt (category='archived',
is_active=false, jurisdiction='UPC') to home the rules.
3. UPDATE all 40 rules → archive pt + lifecycle_state='archived' +
is_active=false. Captured in paliad.deadline_rule_audit via the
mig 079 trigger.
4. DELETE the 7 litigation rows from paliad.proceeding_types (now
safe — nothing references them).
5. Hard assertions: 0 litigation rows survive, exactly 40 rules on
the archive pt, every snapshot row matches a surviving rule by id.
Critical FK note: deadline_rules.proceeding_type_id is ON DELETE CASCADE
→ proceeding_types(id). A naive DELETE of the 7 litigation rows would
cascade-delete all 40 rules and break the FK from the 1 live deadline
("Lecker Frist", completed) that still references inf.rejoin/INF.
Re-homing the rules before deleting the pt rows is mandatory.
Verified via BEGIN..ROLLBACK against live DB: assertions pass, all 30
intra-litigation parent_id chains preserved, the live deadline FK
stays valid.
Test impact:
internal/services/project_service_test.go:72 used to look up
category='litigation' AND code='INF' to exercise the Slice 5 negative
case. Post-mig-093 that lookup returns NULL. Rewritten to fetch any
category <> 'fristenrechner' row (the _archived_litigation pt is the
canonical post-093 row); defence-in-depth coverage of both the Go
service guard and the mig 088 SQL trigger is preserved.
SURFACED FOR LEGAL REVIEW (4 coverage questions the audit found, to be
triaged as follow-up tasks):
1. inf.prelim (Preliminary Objection, RoP 19, 1 month) — not present
on UPC_INF. Possible coverage gap; legal review to decide whether
to add it to the fristenrechner ruleset.
2. inf.appeal / rev.appeal / ccr.appeal as cross-proceeding spawns
into UPC_APP (2 months, UPC.RoP.220.1) — fristenrechner UPC_APP
currently starts standalone with no spawn from UPC_INF/UPC_REV.
Possible UX gap; Pipeline-A versions had
spawn_proceeding_type_id=NULL so they weren't functional spawns
either.
3. ccr.amend / rev.amend (spawn rules) — superseded by
inf.app_to_amend / rev.app_to_amend on UPC_INF / UPC_REV. Safe to
drop; no action needed.
4. zpo.klage / zpo.vertanz / zpo.klageerw / zpo.berufung — no UPC
analogue; redundant with the DE_INF / DE_INF_OLG / DE_INF_BGH and
DE_NULL / DE_NULL_BGH chains. Safe to drop; no action needed.
Files:
internal/db/migrations/093_retire_litigation_category.up.sql (new)
internal/db/migrations/093_retire_litigation_category.down.sql (new)
internal/services/project_service_test.go (test rewrite)
Phase 3 Slice 8 part 1 — wire the instance_level data field (mig 080
column, shipped in Slice 1) through the project service + handler.
- CreateProjectInput / UpdateProjectInput gain InstanceLevel *string.
Empty string is the explicit "clear" sentinel.
- validateInstanceLevel + nullableInstanceLevel helpers mirror the
OurSide pattern. Allowed values per mig 080 CHECK: 'first' |
'appeal' | 'cassation' | NULL.
- Service rejects bad values with ErrInvalidInput (existing handler
error-mapping surfaces this as HTTP 400 with the standard message).
- projectColumns SELECT now includes instance_level so reads
populate the field; Project struct already has the field from
Slice 1.
- handleCreateProject accepts instance_level from the raw map; Update
handler uses the standard JSON decoder into UpdateProjectInput.
Live-DB test exercises:
- Create with instance_level='first' → roundtrips.
- Update to 'appeal' → roundtrips.
- Update to '' → NULL after the trip.
- Update to 'supreme' → ErrInvalidInput.
The DB CHECK on mig 080 is the defence-in-depth backstop should an
SQL-direct INSERT bypass the service.
Live-DB test (TEST_DATABASE_URL-gated) for Phase 3 Slice 5 that
covers the full chain:
1. Migration smoke: post-mig 087, no project points at a
non-fristenrechner-category proceeding_types row.
2. ProjectService.Create with a litigation-category id returns
ErrInvalidProceedingTypeCategory (service-layer guard fires
before any DB write).
3. mig 088 trigger rejects a raw INSERT that bypasses the Go
service — defence-in-depth assertion. Errors on the trigger
raise; test asserts a non-nil error.
4. Fristenrechner-category id (UPC_INF) succeeds. The created
project carries the expected proceeding_type_id.
The four sub-assertions hit each layer of the guard chain (picker
filter → service guard → DB trigger) plus the migration smoke. Any
regression in the chain surfaces here before the deploy.
Tests + main build clean; live test skips when TEST_DATABASE_URL
is unset.