From 13166cd7842bea052d5fb63fb90ea338d4e3f8c7 Mon Sep 17 00:00:00 2001 From: mAi Date: Mon, 1 Jun 2026 15:58:32 +0200 Subject: [PATCH] =?UTF-8?q?fix(submissions):=20t-paliad-365=20picker=20qua?= =?UTF-8?q?lity=20=E2=80=94=20re-kind=208=20court-act=20rules=20(mig=20164?= =?UTF-8?q?)=20+=20group-header=20contrast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1: 8 anchor/trigger rows (Zustellung des Urteils / Veröffentlichung der Erteilung) were mislabeled event_type='filing'+primary_party='both' and leaked into the /submissions/new draftable-submission picker. Migration 164 re-kinds them to event_type='decision'+primary_party='court', aligning them with the 16 sibling court-act rows the model already has; the picker's event_type='filing' filter then excludes them. Defensive guard added to loadSubmissionCatalog (primary_party IS DISTINCT FROM 'court') as belt-and-braces against future drift. Safety: the only event_type coupling for these rows is ruleAnchorKind (projection_service.go) — they now anchor as appointments (correct, sibling- consistent); deadline computation keys off is_court_set not event_type, and the child sequence guard parentHasAnchoredActual UNIONs both anchor tables, so no chain breaks. Verified: catalog 113->105, 8 court acts gone, 48 'both' party submissions (incl. UPC appeal briefs) retained. P2: the grouped picker table already renders a correct colspan group-header row (a911a2d); the defect was contrast — the band used --color-bg-subtle, the SAME token as the thead, so groups read as undelimited floating text. New --color-bg-group-header token (cool/deeper in light, raised-cream in dark) + heavier top divider + neutral left accent + darker label make each proceeding a distinct section. Live deployed bundle confirmed current (not stale). --- frontend/src/styles/global.css | 18 ++++-- .../164_rekind_court_act_rules.down.sql | 20 ++++++ .../164_rekind_court_act_rules.up.sql | 64 +++++++++++++++++++ internal/handlers/submissions.go | 6 ++ 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 internal/db/migrations/164_rekind_court_act_rules.down.sql create mode 100644 internal/db/migrations/164_rekind_court_act_rules.up.sql diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css index 36f2c05..36ab2da 100644 --- a/frontend/src/styles/global.css +++ b/frontend/src/styles/global.css @@ -26,6 +26,7 @@ theme swap for free. */ --color-bg: var(--hlc-cream); --color-bg-subtle: #f7f3f0; /* slightly off-cream for table headers / soft surfaces */ + --color-bg-group-header: rgb(var(--hlc-midnight-rgb) / 0.055); /* cool band for grouped-table section headers — deeper + cooler than the warm-cream thead so groups read as distinct sections (t-paliad-365 P2) */ --color-bg-lime-tint: rgb(var(--hlc-lime-rgb) / 0.10); --color-surface: #ffffff; /* cards stay white for contrast */ --color-surface-2: #fafafa; /* slightly raised surface (alt rows, code blocks) */ @@ -180,6 +181,7 @@ :root[data-theme="dark"] { --color-bg: var(--hlc-midnight); --color-bg-subtle: #00304a; /* slightly raised for table headers */ + --color-bg-group-header: rgb(var(--hlc-cream-rgb) / 0.08); /* raised cream band over midnight — distinct from the thead's #00304a (t-paliad-365 P2) */ --color-bg-lime-tint: rgb(var(--hlc-lime-rgb) / 0.12); --color-surface: #0a3047; /* card surface — distinct from body */ --color-surface-2: #0d3a55; /* one step further raised */ @@ -6064,16 +6066,24 @@ dialog.modal::backdrop { proceeding with a lime border so the lawyer sees their primary context at a glance. */ .entity-table-group-header th { - padding: 0.65rem 1rem; + padding: 0.85rem 1rem 0.7rem; text-align: left; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; - color: var(--color-text-muted); - background: var(--color-bg-subtle); - border-top: 1px solid var(--color-border); + color: var(--color-text); + /* t-paliad-365 P2: the group-header band used --color-bg-subtle, the SAME + token as the thead, so over the cream page each proceeding header read as + undelimited floating text and the columns looked broken. A distinct, + cooler/deeper band + a heavier top rule + a neutral left accent make each + proceeding read as its own clearly-separated section. The --own modifier + (lime accent, below) still overrides this to mark the project's own + proceeding. */ + background: var(--color-bg-group-header); + border-top: 2px solid var(--color-border-strong); border-bottom: 1px solid var(--color-border); + border-left: 3px solid var(--color-text-muted); } .entity-table tbody tr.entity-table-group-header, diff --git a/internal/db/migrations/164_rekind_court_act_rules.down.sql b/internal/db/migrations/164_rekind_court_act_rules.down.sql new file mode 100644 index 0000000..b9ba370 --- /dev/null +++ b/internal/db/migrations/164_rekind_court_act_rules.down.sql @@ -0,0 +1,20 @@ +-- Down for 164_rekind_court_act_rules — restore the pre-fix mislabel +-- (event_type='filing', primary_party='both') on the 8 court/trigger rows. +-- Scoped to the re-kinded state so it only reverses this migration's effect. + +UPDATE paliad.deadline_rules_unified + SET event_type = 'filing', + primary_party = 'both', + updated_at = now() + WHERE submission_code IN ( + 'de.inf.bgh.urteil_olg', + 'de.inf.olg.urteil_lg', + 'de.null.bgh.urteil_bpatg', + 'dpma.appeal.bgh.entsch_bpatg', + 'dpma.appeal.bpatg.entscheidung', + 'epa.opp.boa.entsch', + 'dpma.opp.dpma.publish', + 'epa.opp.opd.grant' + ) + AND event_type = 'decision' + AND primary_party = 'court'; diff --git a/internal/db/migrations/164_rekind_court_act_rules.up.sql b/internal/db/migrations/164_rekind_court_act_rules.up.sql new file mode 100644 index 0000000..3ddde51 --- /dev/null +++ b/internal/db/migrations/164_rekind_court_act_rules.up.sql @@ -0,0 +1,64 @@ +-- 164_rekind_court_act_rules — t-paliad-365 (P1), diagnosis t-paliad-363. +-- +-- Re-kind 8 anchor/trigger rows in paliad.deadline_rules_unified that are +-- MISLABELED event_type='filing' + primary_party='both'. They are COURT / +-- registry acts — the service of a lower-court judgment ("Zustellung … Urteil" +-- / "Zustellung … Entscheidung") or the publication of grant ("Veröffentlichung +-- der Erteilung") — that START a deadline chain. They are NOT party +-- submissions, yet they surfaced in the global /submissions/new picker, whose +-- catalog query filters event_type='filing' (loadSubmissionCatalog, +-- internal/handlers/submissions.go). m noticed "Service of OLG Judgment" +-- (de.inf.bgh.urteil_olg) listed as draftable. +-- +-- The model already carries a correct court-act tier: 16 sibling rows with +-- event_type='decision' + primary_party='court' (de.inf.olg.urteil_olg, +-- upc.inf.cfi.decision, …). This migration aligns the 8 mislabeled rows with +-- that tier. The picker filter (event_type='filing') then excludes them +-- automatically — no handler change is required for the core fix (a defensive +-- primary_party guard is added in submissions.go as belt-and-braces). +-- +-- m's fork LOCKED to data-correction (t-paliad-363 §P1, fork i). +-- +-- submission_code is KEPT (the 16 sibling decision rows keep theirs as stable +-- anchor identifiers; the deadline chain links via parent_id (uuid FK), which +-- this migration does not touch — so anchoring is unaffected). +-- +-- ── SAFETY (grep of the deadline engine, t-paliad-365 Step 1) ────────────── +-- The ONLY code coupled to event_type for these rows is ruleAnchorKind +-- (projection_service.go:1825): event_type IN ('hearing','decision','order') +-- → the rule anchors as a paliad.appointments row; everything else anchors as +-- a paliad.deadlines row. After this migration, clicking "Datum setzen" on one +-- of these 8 court/trigger events records it as an APPOINTMENT (milestone) +-- instead of a deadline — which is the semantically correct, sibling-consistent +-- behaviour. +-- * Deadline COMPUTATION is NOT affected: the forward projection +-- (computeProjections) keys off the is_court_set boolean, never event_type. +-- * Child chains are NOT broken: the sequence guard parentHasAnchoredActual +-- (projection_service.go:1803) UNIONs paliad.deadlines AND +-- paliad.appointments, so a child (e.g. Revisionsfrist) still finds its +-- re-kinded parent's anchor regardless of which table it lands in. +-- * Existing data: exactly 1 paliad.deadlines row is already anchored to one +-- of these 8 rules (0 appointments) as of 2026-06-01. It stays valid and +-- is still found by the dual-table guard. The only cosmetic effect is that +-- a future RE-anchor of that same event would write an appointment beside +-- the old deadline; benign, and only if re-anchored. +-- Verdict: re-kinding does not break deadline computation. Surfaced to head. +-- +-- ADDITIVE / data-only. No schema changes. Reversible (see .down.sql). +-- Idempotent: scoped to the mislabeled state (event_type='filing'). + +UPDATE paliad.deadline_rules_unified + SET event_type = 'decision', + primary_party = 'court', + updated_at = now() + WHERE submission_code IN ( + 'de.inf.bgh.urteil_olg', -- Zustellung OLG-Urteil + 'de.inf.olg.urteil_lg', -- Zustellung LG-Urteil + 'de.null.bgh.urteil_bpatg', -- Zustellung BPatG-Urteil + 'dpma.appeal.bgh.entsch_bpatg', -- Zustellung BPatG-Entscheidung + 'dpma.appeal.bpatg.entscheidung',-- Zustellung DPMA-Entscheidung + 'epa.opp.boa.entsch', -- Zustellung der Beschwerdeentscheidung + 'dpma.opp.dpma.publish', -- Veröffentlichung der Erteilung (DPMA) + 'epa.opp.opd.grant' -- Veröffentlichung der Erteilung (EPA) + ) + AND event_type = 'filing'; diff --git a/internal/handlers/submissions.go b/internal/handlers/submissions.go index 973f43e..eed29d3 100644 --- a/internal/handlers/submissions.go +++ b/internal/handlers/submissions.go @@ -205,6 +205,12 @@ func loadSubmissionCatalog(ctx context.Context, projectProceedingTypeID *int) ([ WHERE dr.is_active = true AND dr.lifecycle_state = 'published' AND dr.event_type = 'filing' + -- Defensive guard (t-paliad-365 P1): even if a court/trigger act is + -- ever mis-kinded event_type='filing' again, primary_party='court' + -- keeps it out of the draftable-submission picker. mig 164 re-kinded + -- the 8 known offenders to event_type='decision'+primary_party='court'; + -- this is belt-and-braces against future drift. + AND dr.primary_party IS DISTINCT FROM 'court' AND dr.submission_code IS NOT NULL AND dr.submission_code <> '' AND pt.is_active = true