Backfill applies_to_target: extend merits + order rules to cover Schadensbemessung / Bucheinsicht #134

Open
opened 2026-05-26 13:32:44 +00:00 by mAi · 1 comment
Collaborator

Why

After Slice B1's Berufung unification (m/paliad#124 §18.1), the picker exposes 5 appeal targets:

target track
Endentscheidung merits — R.118 / R.224 (2mo + 4mo)
Kostenentscheidung cost-decision — leave-to-appeal, R.221
Anordnung 15-day order, R.220.2
Schadensbemessung EMPTY today — enum exists, no rules
Bucheinsicht EMPTY today — enum exists, no rules

m's call (2026-05-26 ~15:31): extend applies_to_target arrays on the existing rules so Schadensbemessung uses the merits track and Bucheinsicht uses the order track — provided this is legally correct against the actual rules we have.

The UPC R.224 appeal procedure (2-month notice + 4-month grounds) is uniform — it doesn't vary by the kind of substantive decision being appealed. Only the anchor differs (which decision triggers the timeline).

  • Schadensbemessung-Entscheidung is a substantive decision rendered under the same R.118 framework as the merits Endentscheidung → same R.224 track. Extend the 7 applies_to_target=['endentscheidung'] rules to ['endentscheidung','schadensbemessung'].
  • Bucheinsicht-Anordnung is an order under R.190 / R.196 → same R.220.2 order-appeal track. Extend the 7 applies_to_target=['anordnung'] rules to ['anordnung','bucheinsicht'].

Worker MUST audit the live paliad.deadline_rules rows for upc.apl.unified first. For each of the 16 rules currently bound to it, read the rule_code / legal_source / description and confirm the proposed extension fits. Surface any rule where the extension is not obvious — do NOT extend blindly.

Migration scope

  • Migration 13X (verify MAX(version) at write time) — single UPDATE paliad.deadline_rules SET applies_to_target = applies_to_target || 'schadensbemessung'::text WHERE ... per target, with strict WHERE clauses.
  • MUST call SELECT set_config('paliad.audit_reason', 'mig 13X: t-paliad-... — extend applies_to_target for Schadensbemessung/Bucheinsicht per m/paliad#134', true); at the top of .up.sql and .down.sql (deadline_rules audit trigger). This is one of the three traps that bit mig 134 today.
  • Audit-first RAISE NOTICE listing the rules that will be touched and their pre/post applies_to_target. RAISE EXCEPTION if any unexpected row appears.
  • Down migration removes the added values via array_remove(applies_to_target, '...') keyed by the same WHERE clauses.
  • No new columns, no destructive ops, no deadline_rules.updated_at writes (table has no such column — fourth trap to avoid).

Acceptance

  1. Worker posts the audit findings as a Gitea comment on this issue (which rules they propose to extend, with rule_code + legal_source + description).
  2. https://paliad.de/tools/verfahrensablauf?appellant=defendant&target=schadensbemessung and ?target=bucheinsicht both render the appropriate timeline (R.224 / R.220 events).
  3. Existing endentscheidung / anordnung timelines unchanged (no rule was removed from those targets).
  4. Migration applies cleanly via the in-process boot path.
  5. go test ./... green, including any lookup_events / Berufung tests.

Anti-patterns to avoid (lessons from today's mig 134)

  • DON'T assume a column exists — proceeding_types.updated_at doesn't.
  • DON'T skip set_config('paliad.audit_reason', …) — every UPDATE paliad.deadline_rules triggers the audit barrier.
  • DON'T cross-check against the design doc alone — the live DB drifted by 23 rows in 24h. Audit live before drafting.
## Why After Slice B1's Berufung unification (m/paliad#124 §18.1), the picker exposes 5 appeal targets: | target | track | |---|---| | Endentscheidung | merits — R.118 / R.224 (2mo + 4mo) | | Kostenentscheidung | cost-decision — leave-to-appeal, R.221 | | Anordnung | 15-day order, R.220.2 | | **Schadensbemessung** | EMPTY today — enum exists, no rules | | **Bucheinsicht** | EMPTY today — enum exists, no rules | m's call (2026-05-26 ~15:31): extend `applies_to_target` arrays on the existing rules so Schadensbemessung uses the merits track and Bucheinsicht uses the order track — **provided this is legally correct against the actual rules we have**. ## Legal premise (worker MUST verify before drafting the migration) The UPC R.224 appeal procedure (2-month notice + 4-month grounds) is *uniform* — it doesn't vary by the kind of substantive decision being appealed. Only the *anchor* differs (which decision triggers the timeline). - **Schadensbemessung-Entscheidung** is a substantive decision rendered under the same R.118 framework as the merits Endentscheidung → same R.224 track. Extend the 7 `applies_to_target=['endentscheidung']` rules to `['endentscheidung','schadensbemessung']`. - **Bucheinsicht-Anordnung** is an order under R.190 / R.196 → same R.220.2 order-appeal track. Extend the 7 `applies_to_target=['anordnung']` rules to `['anordnung','bucheinsicht']`. **Worker MUST audit the live `paliad.deadline_rules` rows for `upc.apl.unified` first.** For each of the 16 rules currently bound to it, read the `rule_code` / `legal_source` / `description` and confirm the proposed extension fits. Surface any rule where the extension is *not* obvious — do NOT extend blindly. ## Migration scope - Migration 13X (verify `MAX(version)` at write time) — single `UPDATE paliad.deadline_rules SET applies_to_target = applies_to_target || 'schadensbemessung'::text WHERE ...` per target, with strict WHERE clauses. - **MUST** call `SELECT set_config('paliad.audit_reason', 'mig 13X: t-paliad-... — extend applies_to_target for Schadensbemessung/Bucheinsicht per m/paliad#134', true);` at the top of `.up.sql` and `.down.sql` (deadline_rules audit trigger). This is one of the three traps that bit mig 134 today. - Audit-first RAISE NOTICE listing the rules that will be touched and their pre/post `applies_to_target`. RAISE EXCEPTION if any unexpected row appears. - Down migration removes the added values via `array_remove(applies_to_target, '...')` keyed by the same WHERE clauses. - No new columns, no destructive ops, no `deadline_rules.updated_at` writes (table has no such column — fourth trap to avoid). ## Acceptance 1. Worker posts the audit findings as a Gitea comment on this issue (which rules they propose to extend, with rule_code + legal_source + description). 2. `https://paliad.de/tools/verfahrensablauf?appellant=defendant&target=schadensbemessung` and `?target=bucheinsicht` both render the appropriate timeline (R.224 / R.220 events). 3. Existing endentscheidung / anordnung timelines unchanged (no rule was *removed* from those targets). 4. Migration applies cleanly via the in-process boot path. 5. `go test ./...` green, including any lookup_events / Berufung tests. ## Anti-patterns to avoid (lessons from today's mig 134) - DON'T assume a column exists — `proceeding_types.updated_at` doesn't. - DON'T skip `set_config('paliad.audit_reason', …)` — every `UPDATE paliad.deadline_rules` triggers the audit barrier. - DON'T cross-check against the design doc alone — the live DB drifted by 23 rows in 24h. Audit live before drafting.
mAi self-assigned this 2026-05-26 13:32:44 +00:00
Author
Collaborator

Audit: live paliad.deadline_rules for upc.apl.unified (proceeding_type_id=160)

16 active rules — exact distribution from prod DB just now:

Merits track — applies_to_target=['endentscheidung'] (7 rules) → propose extend with 'schadensbemessung'

rule_code legal_source name duration
RoP.224.1.a UPC.RoP.224.1.a Berufungseinlegung 2 months
RoP.224.2.a UPC.RoP.224.2.a Berufungsbegründung 4 months
RoP.235.1 UPC.RoP.235.1 Berufungserwiderung 3 months
RoP.237 UPC.RoP.237 Anschlussberufung 3 months
RoP.238.1 UPC.RoP.238.1 Erwiderung Anschlussberufung 2 months
(no rule_code) (none) Mündliche Verhandlung (hearing anchor child)
(no rule_code) (none) Entscheidung (decision anchor child)

Legal fit (Schadensbemessung): Schadensbemessungs-Entscheidung is a substantive judgment rendered under the same R.118 framework as an Endentscheidung (R.118.4 explicitly covers damages quanta as part of the Hauptentscheidung family). The R.224 appeal timeline does not vary by which substantive R.118 decision is being appealed — only the anchor (the decision being challenged) differs. Therefore all 7 merits-track rules transfer cleanly. The two no-rule_code anchor children (hearing, decision) are part of the merits procedural flow and apply to any R.224 appeal.

Order track — applies_to_target=['anordnung'] (7 rules) → propose extend with 'bucheinsicht'

rule_code legal_source name duration
RoP.220.2 UPC.RoP.220.2 Berufung mit Zulassung 15 days
RoP.220.3 UPC.RoP.220.3 Antrag auf Ermessensüberprüfung 15 days
RoP.224.2.b UPC.RoP.224.2.b Berufungsbegründung (Orders Track) 15 days
RoP.235.2 UPC.RoP.235.2 Berufungserwiderung (Orders Track) 15 days
RoP.237 UPC.RoP.237 Anschlussberufung (Orders Track) 15 days
RoP.238.2 UPC.RoP.238.2 Erwiderung Anschlussberufung (Orders Track) 15 days
(no rule_code) (none) Anordnung / angegriffene Entscheidung (decision anchor child)

Legal fit (Bucheinsicht): Bucheinsicht-Anordnungen sind Anordnungen unter R.190 (Lay-open-books-Anordnung als Teil des damage-assessment-Verfahrens) bzw. R.196 (vorläufige Anordnungen). Beide sind Anordnungen im Sinne von R.220.2: appealable mit Zulassung (R.220.2 — 15 Tage) oder via diskretionäre Überprüfung (R.220.3). Der gesamte Order-Track (R.224.2.b Begründung, R.235.2 Erwiderung, R.237/238.2 Anschlussberufung) ist uniform — er hängt nicht vom Inhalt der angegriffenen Anordnung ab. Alle 7 Order-Track-Rules übertragen sich daher sauber.

Cost track — applies_to_target=['kostenentscheidung'] (2 rules) → NOT touched

rule_code legal_source name
RoP.221.1 UPC.RoP.221.1 Antrag auf Berufungszulassung
(no rule_code) (none) Kostenfestsetzungsbeschluss (decision anchor child)

Intentional non-action: the kostenentscheidung leave-to-appeal track is distinct from both merits and order procedures and is not part of this slice.

Ambiguous cases

None surfaced. Every endentscheidung rule is a generic R.224 merits-track step (or an anchor-child); every anordnung rule is a generic R.220/224/235/237/238 order-track step (or an anchor-child). No rule carries content that is specific to a particular type of underlying decision/order — they describe procedural steps, not substantive subject matter.

Plan

Migration 137 (next available; MAX(version)=106 in paliad_schema_migrations, last file is 136_procedural_events_additive):

  • set_config('paliad.audit_reason', 'mig 137: t-paliad-303 — extend applies_to_target for Schadensbemessung (merits) + Bucheinsicht (order) per m/paliad#134', true) at top of both up + down.
  • Audit-first DO block: list all rules to be touched with pre/post applies_to_target, RAISE EXCEPTION if any unexpected row shape appears.
  • Two narrow UPDATEs:
    • SET applies_to_target = applies_to_target || 'schadensbemessung'::text WHERE pt.code='upc.apl.unified' AND 'endentscheidung'=ANY(applies_to_target) AND NOT 'schadensbemessung'=ANY(applies_to_target) AND is_active=true — expect 7 rows
    • SET applies_to_target = applies_to_target || 'bucheinsicht'::text WHERE pt.code='upc.apl.unified' AND 'anordnung'=ANY(applies_to_target) AND NOT 'bucheinsicht'=ANY(applies_to_target) AND is_active=true — expect 7 rows
  • Post-sanity: assert counts = 7 + 7, RAISE EXCEPTION on mismatch.
  • Down: array_remove(applies_to_target, 'schadensbemessung') / array_remove(..., 'bucheinsicht') with same WHERE.
  • No updated_at writes (deadline_rules has the column but the migration leaves it as-is — single-purpose touch, not a metadata refresh).

Test update: internal/services/lookup_events_test.go:147 (appeal_target=schadensbemessung returns empty) becomes the inverse — schadensbemessung must now return ≥1 anchor row from upc.apl.unified. Adding a parallel bucheinsicht assertion.

Proceeding with the migration.

## Audit: live `paliad.deadline_rules` for `upc.apl.unified` (proceeding_type_id=160) 16 active rules — exact distribution from prod DB just now: ### Merits track — `applies_to_target=['endentscheidung']` (7 rules) → propose extend with `'schadensbemessung'` | rule_code | legal_source | name | duration | |---|---|---|---| | `RoP.224.1.a` | `UPC.RoP.224.1.a` | Berufungseinlegung | 2 months | | `RoP.224.2.a` | `UPC.RoP.224.2.a` | Berufungsbegründung | 4 months | | `RoP.235.1` | `UPC.RoP.235.1` | Berufungserwiderung | 3 months | | `RoP.237` | `UPC.RoP.237` | Anschlussberufung | 3 months | | `RoP.238.1` | `UPC.RoP.238.1` | Erwiderung Anschlussberufung | 2 months | | _(no rule_code)_ | _(none)_ | Mündliche Verhandlung (hearing anchor child) | — | | _(no rule_code)_ | _(none)_ | Entscheidung (decision anchor child) | — | **Legal fit (Schadensbemessung):** Schadensbemessungs-Entscheidung is a substantive judgment rendered under the same R.118 framework as an Endentscheidung (R.118.4 explicitly covers damages quanta as part of the Hauptentscheidung family). The R.224 appeal timeline does not vary by which substantive R.118 decision is being appealed — only the anchor (the decision being challenged) differs. Therefore all 7 merits-track rules transfer cleanly. The two no-rule_code anchor children (hearing, decision) are part of the merits procedural flow and apply to any R.224 appeal. ### Order track — `applies_to_target=['anordnung']` (7 rules) → propose extend with `'bucheinsicht'` | rule_code | legal_source | name | duration | |---|---|---|---| | `RoP.220.2` | `UPC.RoP.220.2` | Berufung mit Zulassung | 15 days | | `RoP.220.3` | `UPC.RoP.220.3` | Antrag auf Ermessensüberprüfung | 15 days | | `RoP.224.2.b` | `UPC.RoP.224.2.b` | Berufungsbegründung (Orders Track) | 15 days | | `RoP.235.2` | `UPC.RoP.235.2` | Berufungserwiderung (Orders Track) | 15 days | | `RoP.237` | `UPC.RoP.237` | Anschlussberufung (Orders Track) | 15 days | | `RoP.238.2` | `UPC.RoP.238.2` | Erwiderung Anschlussberufung (Orders Track) | 15 days | | _(no rule_code)_ | _(none)_ | Anordnung / angegriffene Entscheidung (decision anchor child) | — | **Legal fit (Bucheinsicht):** Bucheinsicht-Anordnungen sind Anordnungen unter R.190 (Lay-open-books-Anordnung als Teil des damage-assessment-Verfahrens) bzw. R.196 (vorläufige Anordnungen). Beide sind Anordnungen im Sinne von R.220.2: appealable mit Zulassung (R.220.2 — 15 Tage) oder via diskretionäre Überprüfung (R.220.3). Der gesamte Order-Track (R.224.2.b Begründung, R.235.2 Erwiderung, R.237/238.2 Anschlussberufung) ist uniform — er hängt nicht vom Inhalt der angegriffenen Anordnung ab. Alle 7 Order-Track-Rules übertragen sich daher sauber. ### Cost track — `applies_to_target=['kostenentscheidung']` (2 rules) → NOT touched | rule_code | legal_source | name | |---|---|---| | `RoP.221.1` | `UPC.RoP.221.1` | Antrag auf Berufungszulassung | | _(no rule_code)_ | _(none)_ | Kostenfestsetzungsbeschluss (decision anchor child) | Intentional non-action: the kostenentscheidung leave-to-appeal track is distinct from both merits and order procedures and is not part of this slice. ### Ambiguous cases None surfaced. Every endentscheidung rule is a generic R.224 merits-track step (or an anchor-child); every anordnung rule is a generic R.220/224/235/237/238 order-track step (or an anchor-child). No rule carries content that is specific to a particular type of underlying decision/order — they describe procedural steps, not substantive subject matter. ### Plan Migration **137** (next available; `MAX(version)=106` in `paliad_schema_migrations`, last file is `136_procedural_events_additive`): - `set_config('paliad.audit_reason', 'mig 137: t-paliad-303 — extend applies_to_target for Schadensbemessung (merits) + Bucheinsicht (order) per m/paliad#134', true)` at top of both up + down. - Audit-first DO block: list all rules to be touched with pre/post `applies_to_target`, RAISE EXCEPTION if any unexpected row shape appears. - Two narrow UPDATEs: - `SET applies_to_target = applies_to_target || 'schadensbemessung'::text WHERE pt.code='upc.apl.unified' AND 'endentscheidung'=ANY(applies_to_target) AND NOT 'schadensbemessung'=ANY(applies_to_target) AND is_active=true` — expect 7 rows - `SET applies_to_target = applies_to_target || 'bucheinsicht'::text WHERE pt.code='upc.apl.unified' AND 'anordnung'=ANY(applies_to_target) AND NOT 'bucheinsicht'=ANY(applies_to_target) AND is_active=true` — expect 7 rows - Post-sanity: assert counts = 7 + 7, RAISE EXCEPTION on mismatch. - Down: `array_remove(applies_to_target, 'schadensbemessung')` / `array_remove(..., 'bucheinsicht')` with same WHERE. - No `updated_at` writes (deadline_rules has the column but the migration leaves it as-is — single-purpose touch, not a metadata refresh). Test update: `internal/services/lookup_events_test.go:147` (`appeal_target=schadensbemessung returns empty`) becomes the inverse — schadensbemessung must now return ≥1 anchor row from `upc.apl.unified`. Adding a parallel bucheinsicht assertion. Proceeding with the migration.
mAi added the
done
label 2026-05-26 13:44:27 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#134
No description provided.