Commit Graph

1288 Commits

Author SHA1 Message Date
mAi
1844df3ae6 Merge: t-paliad-340 B0 — Scenario DB foundation (m/paliad#153)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 23:53:49 +02:00
mAi
0f3c30a647 feat(scenario-builder): B0 schema foundation + minimal API (m/paliad#153)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
t-paliad-340 — B0 of edison's 7-slice train (PRD §7.1). DB-only: schema
+ RLS land, dev-only test route exercises the surface, no user-facing
change. B1 wires the actual builder UI on top.

Migration 157 (additive on the legacy mig-145 scenarios table — 0 rows
in prod, safe to relax):
- paliad.scenarios gets owner_id / status / origin_project_id /
  promoted_project_id / stichtag / notes. spec drops NOT NULL and the
  scenarios_unique_per_scope constraint drops (the builder allows
  multiple scratch + Unbenanntes Szenario rows per user).
- New tables: scenario_proceedings, scenario_events, scenario_shares.
- paliad.projects.origin_scenario_id for the promote-to-project audit
  trail (the FK lands now; the wizard ships in B5).
- paliad.can_see_scenario(uuid) STABLE SECURITY DEFINER helper covering
  owner / share / global_admin / two legacy paths.
- Replacement RLS on scenarios + RLS on the three new tables; legacy
  service + handlers stay live and unchanged.

PRD §5.1 deviations called out in the migration header:
- proceeding_type_id is integer (live schema), not uuid (PRD draft).
- FK target is paliad.users, matching the rest of paliad's schema.

Go surface:
- ScenarioBuilderService — list/create/get-deep/patch scenarios,
  add/patch/delete proceedings, add/patch/delete events,
  add/delete shares. Writes wrap in transactions with set_config(
  paliad.audit_reason, ..., true) per event_choice_service.go pattern.
- /api/builder/scenarios/* — handlers register under a builder/
  prefix so the legacy /api/scenarios surface still works.
- /dev/scenario-builder — single-page HTML form gated to
  PaliadinOwnerEmail, exercises the B0 surface without Postman.
- Live-DB integration test (TEST_DATABASE_URL gated) covers
  create + list + deep-get + share + visibility negatives + patch.

Audit-first: every DDL block ran clean via BEGIN/ROLLBACK against
the live DB before commit; end-to-end sanity (insert chain + CHECK
constraints + CASCADE-on-delete) verified via the Supabase MCP.

bun build clean. go vet + go test -short ./... green.
2026-05-27 23:50:14 +02:00
mAi
2c2b93bc7c Merge: t-paliad-339 — PRD for Procedures Litigation Builder (m/paliad#153)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 23:04:07 +02:00
mAi
661d87273c docs(plans): PRD — Procedures Litigation Builder (m/paliad#153)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
PRD for the columnar litigation planner replacing today's 4-tab catalog
at /tools/procedures with a Litigation Builder backed by a new Scenario
DB. Captures 20 chip-picker decisions (5 batches via AskUserQuestion)
covering: unified-builder shape with 3 entry modes (cold-open /
event-triggered / Akte), separate paliad.scenarios table with
multi-proceeding constellations, auto-save + named-list, per-proceeding
flags + perspective + Detailgrad, 3-state event cards
(planned/filed/skipped), per-event-card optional horizon, vertical
stacked column-triplets with inline spawn children, universal search
(events + scenarios + Akten), 3-step promote-to-project wizard,
read-only team sharing, desktop v1 + mobile basic-read.

Includes data model deltas (4 new tables + 1 column on
paliad.projects), 6-slice migration plan from the current live U0-U4
catalog, and coder hand-off notes. Cross-proceeding peer triggers and
DE/EPA/DPMA full expansion deferred to v1.1.
2026-05-27 23:00:24 +02:00
mAi
ed3c5d1f32 Revert "Merge: t-paliad-338 T1 — workflow-tracker shell replaces catalog (m/paliad#152)"
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
This reverts commit 2e6427dca6, reversing
changes made to 9fe06094a8.
2026-05-27 22:09:06 +02:00
mAi
be570c2fd0 Revert "Merge: t-paliad-338 T3-T5 — workflow-tracker Akte/polish/cleanup (m/paliad#152)"
This reverts commit 6506d7d862, reversing
changes made to 2e6427dca6.
2026-05-27 22:09:06 +02:00
mAi
58692513a8 Revert "Merge: tracker filter pill dark-mode contrast (m/paliad#152)"
This reverts commit 702f786771, reversing
changes made to 6506d7d862.
2026-05-27 22:09:06 +02:00
mAi
702f786771 Merge: tracker filter pill dark-mode contrast (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 22:03:00 +02:00
mAi
93c664c865 fix(procedures): tracker filter pill — dark-mode contrast (m/paliad#152)
Selected .tracker-pill.is-active used --color-accent-fg, which in dark
mode resolves to lime → lime text on lime background, unreadable.
Switch to --color-accent-dark (midnight in both modes) so the selected
pill has midnight text on lime in both light + dark. Same pattern as
the older .filter-pill.active rule.
2026-05-27 22:03:00 +02:00
mAi
6506d7d862 Merge: t-paliad-338 T3-T5 — workflow-tracker Akte/polish/cleanup (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 22:00:03 +02:00
mAi
73f49c46ed chore(procedures): T5 — drop dead code from the U0-U4 catalog (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
The workflow tracker (T1-T4) replaces every consumer of the entry-mode
modules. Verified via grep that no non-deleted file imports the
following before removal:

Deleted (10 files):
  - client/fristenrechner-mode-a.ts (Mode A search panel)
  - client/fristenrechner-wizard.ts (Mode B guided wizard)
  - client/fristenrechner-wizard.test.ts
  - client/fristenrechner-result.ts (post-commit result-view)
  - client/fristenrechner-result.test.ts
  - client/verfahrensablauf.ts (Verfahrensablauf panel client)
  - client/views/event-card-choices.ts (per-card choice popover —
    only verfahrensablauf.ts consumed it)
  - client/views/verfahrensablauf-state.ts (URL + storage helpers —
    only verfahrensablauf.ts consumed it)
  - client/views/verfahrensablauf-state.test.ts
  - components/VerfahrensablaufBody.tsx (the 4-tab proceeding picker
    body — no consumer after T1)

Kept (still load-bearing):
  - client/views/verfahrensablauf-core.ts — procedures-tracker uses
    calculateDeadlines + CalculatedDeadline + escHtml + formatDate.
  - client/views/verfahrensablauf-core.test.ts
  - client/verfahrensablauf-detail-mode.ts — procedures-tracker uses
    filterByDetailMode under the per-proceeding "Alle Optionen"
    toggle (T4).
  - client/verfahrensablauf-detail-mode.test.ts

The .css classes (.fristen-wizard-*, .verfahrensablauf-*) still live
in global.css; they're cheap orphans (no selector match in the new
DOM) and a CSS housekeeping pass is outside this train's scope. The
i18n keys (deadlines.flag.*, deadlines.detail.*, deadlines.view.*,
deadlines.side.*) likewise stay — some are used dynamically via tDyn
on the tracker, others remain candidates for a future i18n sweep.

Frontend tests: 217 pass (264 → 217, the deltas are the 3 deleted
test files: fristenrechner-result, fristenrechner-wizard,
verfahrensablauf-state). Build + go vet clean.

t-paliad-338
2026-05-27 21:57:51 +02:00
mAi
c80723fc85 feat(procedures): T4 — appeal-target + Alle Optionen + cross-party + polish (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
The final tracker layer per design §3.4 / §3.6 / §11 polish list:

- Per-proceeding "· Gewählt · / Alle Optionen" toggle (§3.4) lives in
  the card header next to the show/hide button. State persists in
  localStorage per proceeding code, so a page with multiple cards
  can keep one expanded without affecting siblings. Toggle drives the
  detail mode for filterByDetailMode + sets includeHidden=true on the
  calc, so previously-skipped conditional rules re-surface muted.
- Appeal-target chip group (§3.2 #3) renders below the header on
  proceedings with applies_to_target rules — today only upc.apl.unified.
  Endentscheidung / Kostenentscheidung / Anordnung / Schadensbemessung
  / Bucheinsicht. Picking a target re-fetches the calc with the
  appealTarget param so the timeline narrows to the matching subset.
- Cross-party muted treatment (§3.6) — when the find-header Partei
  pill is set, rows whose primary_party is the opposite side render
  with a "Gegen." badge and a muted style. Court / both / informational
  rows are never cross-party.
- "Unselected" + "hidden" styling — under "Alle Optionen" the rules
  that filterByDetailMode stamps __detailUnselected on render dotted
  italic, and previously-skipped (isHidden) rules render at reduced
  opacity. Honest preview of what the user is NOT considering.
- Cross-surface scenario-flag-changed listener — the tracker now reseeds
  its flags state when Mode B / Verfahrensablauf / Verlauf patches the
  same project's flags, so toggling there flows through here without a
  refresh.

Out of T4 (court-set choices_offered chip groups and the court-set date
override from appointments) — those need a follow-up backend pass to
surface the choicesOffered payload on TimelineEntry through the calc
response in a usable shape. The data field exists on CalculatedDeadline
but isn't yet wired to a paint route on the tracker.

t-paliad-338
2026-05-27 21:55:52 +02:00
mAi
1ed75c56e3 feat(procedures): T3 — Akte landing + actuals overlay (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Wires the workflow tracker to projects via ?project=<uuid>, per design
§6.4 + §11.Q5:

- loadAkte fetches /api/projects/{id}, /api/projects/{id}/timeline
  and /api/projects/{id}/scenario-flags in parallel:
    1. Project title + proceeding_type — pre-seeds the Verfahren pill.
    2. Timeline events → ActualsMap keyed by deadline_rule_id with
       status (done / overdue / open / court_set), due / completed
       date, and deadline / appointment ids.
    3. scenario_flags → seeds state.flags so the gating-flag checkboxes
       render in the persisted state. Per-rule rule:<uuid> flags stay
       out of the calc payload (they drive priority deviations via
       isRuleSelected, handled by the existing detail-mode filter).
- Auto-pin: the first render with no explicit ?event= pins the most
  recent status='done' deadline. URL pin (shared link) is preserved.
- Per-node overlay: each node carries the actuals badge — ✓ (done +
  strike-through), ⚠ (overdue + red wash), 📅 (open ≠ projected), ◇
  (open ≡ projected). Date column shows the actual date.
- Fork write-back: PATCH /api/projects/{id}/scenario-flags fires on
  every flag toggle so Mode B / Verlauf / dashboard re-render with the
  same scenario on next visit. Fire-and-forget; UI doesn't wait.
- Find-header summary chips: "Akte: <title>" alongside "Anker: <name>"
  + "{n} Verfahren".

Out of T3 (deferred):
- ?project= picker UI (today's user navigates here from /projects/{id}
  via deep-link).
- Per-rule rule:<uuid> flag write-back (priority deviations) — the
  detail-mode filter doesn't take an interactive toggle yet.
- Cross-surface scenario-flag-changed CustomEvent listener — patching
  fires the event, the tracker just doesn't yet re-render on incoming
  ones (T4 polish).

t-paliad-338
2026-05-27 21:51:42 +02:00
mAi
2e6427dca6 Merge: t-paliad-338 T1 — workflow-tracker shell replaces catalog (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 21:46:57 +02:00
mAi
7945bfb364 feat(procedures): T2 — anchor pin + zoom + multi-proceeding scope (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Layers the anchor / focus interactivity on top of T1's shell per
design §6.1–§6.5:

- Click-to-pin (📌) on every node with a real rule_id sets the anchor.
  Clicking the already-anchored pin un-pins. URL state ?event=<id>.
- Anchored node renders with a "── DU BIST HIER ──" divider beneath
  its meta line + the lime left-band styling. The find-header summary
  surfaces "Anker: <name>" so the user can confirm where they are.
- Fokus chip (🔍) on the anchored node toggles zoom (?zoom=1). Zoom
  renders the anchor's parent chain as a breadcrumb at the top of the
  proceeding card and renders only the anchored subtree below. A
  "{n} weitere Schritte verborgen" footer reports what zoom hid.
- Multi-proceeding scope (§6.5): when an anchor is pinned and >1
  proceeding is visible, non-anchored proceedings auto-collapse to a
  one-line header card with a [zeigen] / [ausblenden] toggle. The
  user's explicit expansions persist for the current anchor; pinning
  a different node clears them.
- Auto-pinning from the search input (T1's single-hit behaviour) now
  routes through onAnchorChanged so the multi-proc scope kicks in
  consistently.

Anchor + zoom state writes through history.replaceState — sharable URL.
Un-pinning clears zoom and restores the full multi-proceeding view
automatically (lastAnchor tracking).

t-paliad-338
2026-05-27 21:46:54 +02:00
mAi
bfb38aab41 feat(procedures): T1 — workflow-tracker shell replaces catalog (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Direct-replace per m's Q7 divergent pick in atlas's design
(docs/design-procedures-workflow-tracker-2026-05-27.md §9): /tools/procedures
drops the 4-tab catalog (U0-U4 shipped this morning) for the single
canonical workflow-tracker shape.

T1 ships:
- Sticky find header — search input, forum / Verfahren / Partei pill
  rows, global Stichtag, live result summary.
- Per-proceeding timeline cards — one card per matched proceeding,
  rendered as a chained tree by parent_id with priority-styled bullets
  (mandatory solid, recommended muted, optional dotted, informational
  faded, court-set blue). Party badge per node.
- Cold-open default: the 6 curated proceedings from design §8 / §11.Q4
  (upc.inf.cfi, upc.rev.cfi, upc.apl.unified, de.inf.lg, epa.opp.opd,
  dpma.opp.dpma) render stacked with a hint above.
- Scenario-flag forks — per-proceeding "Optionen" strip on each card's
  header surfaces the applicable flags (with_ccr, with_amend, with_cci)
  derived from condition_expr or a fallback map. Tick re-runs the calc.
- URL state: ?q, ?forum, ?procs, ?party, ?trigger_date, ?event, ?flags.
  ?event= scroll-highlights the matching node (no zoom yet — T2 layers).
- Legacy ?mode= dropped silently on first state write so bookmarks
  self-clean. /tools/fristenrechner + /tools/verfahrensablauf 301s
  still resolve here.

Floor T1 honours: every catalog workflow it replaces — pick proceeding
(forum + Verfahren pills), search event (search input → auto-narrow +
?event= anchor), wizard narrowing (pills compose), Akte entry
(?project= read-only for T1; full overlay in T3).

Per-node fork placement (the design's stated final shape — checkbox on
the gating node itself, not a card-level strip) is a T2 refinement;
T1 keeps forks scoped per proceeding so they're not the global-page
strip m's bug #5 flagged.

Aux-proceedings inline-expandable (design §5) and the appeal-target
chip group are scoped to T4; the calculator currently doesn't surface
isSpawn / spawnProceedingCode through TimelineEntry to support them.

t-paliad-338
2026-05-27 21:42:31 +02:00
mAi
9fe06094a8 Merge: t-paliad-337 — workflow-tracker design for /tools/procedures (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
atlas shipped the workflow-tracker design after m's 21:01 grilling-round reframe (single timeline-with-forks, find=search+pills+result-timelines, aux inline, zoom from within full tree). 510-line doc, 2 rewrite iterations.

7 Qs answered in 2 batches (4+3). 5 on-recommendation, 2 divergent:
- Q3 (divergent): multi-proceeding anchor scope — auto-collapse other proceedings to header-only (new §6.5)
- Q7 (divergent): migration strategy — direct replace at T1, no feature flag (§9)

4-slice + cleanup train. T1 ships minimum-viable tracker visibly at /tools/procedures, replacing the catalog UI knuth shipped today.

Inventor parks. Head dispatches Sonnet coder (NOT atlas per project memory directive).
2026-05-27 21:14:11 +02:00
mAi
c8f310c62c design(procedures-tracker): fold m's 7 picks — §11 decisions + §6.5 multi-tl anchor + §9 direct-replace migration (t-paliad-337)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
5 picks on-recommendation, 2 diverged:

Q3 (multi-proceeding anchor scope): m picked 'other timelines auto-collapse to header-only' over the recommended 'stay expanded'. Added §6.5 with the header-card render rule.

Q7 (migration cadence): m picked 'direct replace at T1, no flag' over the recommended flag-gated dev. §9 rewritten end-to-end: T1 ships the minimum-viable tracker visibly to users, replacing the catalog UI in the same PR. T2-T4 layer zoom + Akte + polish. T5 is cleanup-only.

The 5 on-rec picks: inline checkbox forks (Q1), sibling-collapse zoom (Q2), 6 curated defaults on cold open (Q4), latest-done-deadline Akte anchor (Q5), global Stichtag (Q6) — all locked as drafted in §1-§8.

Ready for review. Coder gate held; head decides T1 hire.
2026-05-27 21:13:35 +02:00
mAi
7554e86673 design(procedures-tracker): rewrite after m's reframe — single timeline-tree, inline forks, no view toggle (t-paliad-337)
m's grilling-round answers (2026-05-27 21:01):
1. One canonical view (full timeline/tree); zoom is an interaction on it, not a separate view.
2. Forks = everything: scenario flags + optionals + appeal-target + court-set picks.
3. Find = combined affordance: text + pills + result-timelines are one thing.
4. Aux proceedings inline as expandable child timelines.

Doc rewritten end-to-end. The first draft's three-view toggle + Konstellationen drawer + Querverweise drawer + split-pane/breadcrumb apparatus collapses into:
- Sticky find header (search + pill rows + Akte/date)
- N matched proceedings rendered as inline-forked timeline-trees
- Anchor pin + opt-in zoom mode (collapses siblings, breadcrumb back)
- Aux proceedings expand inline at the spawn point

7 open questions in 4+3 batches for AskUserQuestion. T1-T5 migration unchanged in spirit.
2026-05-27 21:05:41 +02:00
mAi
23b151c0f3 design(procedures-tracker): t-paliad-337 shift-1 — workflow-tracker layer for /tools/procedures (m/paliad#152)
m's reframe (2026-05-27 20:43): /tools/procedures should be a workflow
tracker, not a catalog browser. Pick any procedural event, see backward
(predecessors) + self (where I am) + forward (successors), with
scenario_flags as togglable predicates and alternative constellations
explorable.

This shift-1 doc covers:
- 4-tab UX redo (single-pane radio-revealed entry form to fix the
  pre-form-leak bug)
- Anchor visualisation (vertical waterfall with anchor at centre line)
- Three views — Anchor / Verfahren / Konstellationen — toggle preserves
  anchor + scenario state
- Forward walk (current constellation only by default, conditional
  reveal toggle, view-mode toggle reused from atlas P3)
- Backward walk (3 hops default, Akte mode overlays paliad.deadlines
  actuals onto template chain)
- Compound rules drawer (per-anchor Querverweise affordance — column
  shape owned by curie editorial workstream)
- Constellation viewer (inline per-flag preview drawer + full
  Constellation view for browse)
- Akte entry (anchor derives from latest completed deadline)
- Migration: T1-T5 flag-gated dev under ?tracker=1, then hard-cut

Coder gate held. 11 open questions for m staged for AskUserQuestion in
4+4+3 batches. Decisions append as §13 before the
TRACKER DESIGN READY FOR REVIEW signal.
2026-05-27 20:56:51 +02:00
mAi
1718ea2eae Merge: t-paliad-335 — unified /tools/procedures shipped U0-U4 (m/paliad#151)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
knuth shipped all 5 slices in one shift, per cronus's design:

U0 60907e7 — skeleton: new /tools/procedures route + page shell + 4 entry tabs + filter strip with search box + tree+linear-drawer scaffold
U1 0568d34 — fold Mode A (Direkt suchen) — porting fristenrechner-mode-a.ts
U2 c8261da — fold Mode B (Geführt wizard) — porting fristenrechner-wizard.ts
U3 48a07ef — fold Verfahrensablauf tree + 3-way detail filter — porting verfahrensablauf.ts + detail-mode.ts
U4 39c8ef3 — hard-cut 301: /tools/fristenrechner + /tools/verfahrensablauf → /tools/procedures; retire dual surfaces

Net: -4,452 LoC across 20 files (consolidating + dropping legacy). bun build clean, 264 frontend tests pass, go vet + go test ./... clean.

Comment posted on m/paliad#151. Per-project scenario_flags binding from Phase 2 P0 still drives the unified tool's per-rule chips.
2026-05-27 20:35:44 +02:00
mAi
39c8ef343b feat(procedures): U4 hard-cut legacy URLs + retire dual surfaces (m/paliad#151)
Per m's Q11 divergence in the design (no 2-week dual-ship), this slice
flips /tools/fristenrechner and /tools/verfahrensablauf to permanent 301
redirects to /tools/procedures and deletes the legacy frontend pages.
Bookmarks resolve via Location preservation of query params; no
?legacy=1 escape, no in-product affordance pointed back at the retired
URLs after the merge.

Server:
- handleFristenrechnerPage + handleVerfahrensablaufPage now 301 to
  /tools/procedures, carrying any query string through unchanged.
- pillDrillURL in deadline_search_service.go retargets to
  /tools/procedures so freshly indexed search pills land on the new
  page directly (cached snapshots still work via the 301).

Frontend:
- Deleted src/fristenrechner.tsx, src/verfahrensablauf.tsx,
  src/client/fristenrechner.ts.
- src/client/verfahrensablauf.ts loses its DOMContentLoaded auto-boot
  and the now-unused initI18n / initSidebar imports; procedures.ts is
  the sole caller of initVerfahrensablauf().
- frontend/build.ts drops the legacy entrypoints and renderXxx HTML
  outputs.
- Sidebar.tsx, Header.tsx, index.tsx, paliadin-context.ts repointed
  to /tools/procedures.
- Unused nav.fristenrechner / nav.verfahrensablauf /
  tools.verfahrensablauf.* i18n keys removed.

Tests:
- verfahrensablauf_test.go rewritten to assert both legacy URLs return
  301 with the correct Location (query string preserved).
2026-05-27 20:34:54 +02:00
mAi
48a07ef4ef feat(procedures): U3 fold Verfahrensablauf tree + 3-way detail filter (m/paliad#151)
Mounts the full Verfahrensablauf wizard — proceeding picker, perspective
chooser, date inputs, scenario flag rows, detail-mode toggle, view
toggle, timeline-container — under the /tools/procedures "Verfahren
wählen" tab. Per-rule scenario_flags chips (P0 SSoT) and the
Aufnehmen/Entfernen affordances reach the unified page unchanged since
they're delegated handlers on the timeline-container.

Refactor steps:
- Extracted the wizard body markup into a shared TSX component
  (components/VerfahrensablaufBody) used by both verfahrensablauf.tsx
  (legacy) and procedures.tsx (unified). U4 will retire the legacy
  page; the shared component lets U3 ship without code duplication.
- Lifted the verfahrensablauf.ts DOMContentLoaded body into
  initVerfahrensablauf() and re-exported it. The legacy auto-boot
  stays in place but skips itself when #procedures-panel-proceeding
  is present, so the unified page imports the module without
  double-init. procedures.ts calls initVerfahrensablauf() the first
  time the proceeding tab activates, gated by a one-shot flag to
  preserve module-local selectedType / lastResponse across tab
  toggles.
2026-05-27 20:29:05 +02:00
mAi
bb3d7aabd7 Merge: hide archived from admin/procedural-events default view
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 20:27:45 +02:00
mAi
c8390dd02a fix(admin-rules-list): default lifecycle filter to 'published' (hide archived clutter)
m flagged 2026-05-27 20:26: archived rules (e.g. the 5 mig 152 Mängelbeseitigung clones) clutter the /admin/procedural-events default view. They were correctly archived by mig 152 but visually noisy alongside active rules.

Fix: default activeLifecycle = 'published'. The 'Alle' chip still exists for when the user wants to see drafts + archived; 'Archived' chip surfaces them on demand. Initial view shows only the active corpus.
2026-05-27 20:27:45 +02:00
mAi
c8261da492 feat(procedures): U2 fold Mode B (Geführt wizard) (m/paliad#151)
Mounts mountWizard() into #procedures-panel-wizard when the Geführt tab
activates. Same 5-row wizard, same backend (event search + follow-ups
probe) as the legacy /tools/fristenrechner. On R4 launchResult, the
wizard hands off to mountResultView which renders into the same
overhaul-root inside the panel.

The wizard renders into #fristen-overhaul-mode-host while Mode A and
the result view write into #fristen-overhaul-root. To keep those IDs
unique in the DOM — both modes look up via document.getElementById —
the host scaffold is no longer static on the search panel. The new
installOverhaulHost() helper tears down any existing host and installs
a fresh one inside the active tab's panel before each mount, so two
parallel hosts can't cross-wire when the user toggles between the
Direkt-suchen and Geführt tabs.

The U1/U2 placeholders are dropped from the panel markup since the
panels are populated dynamically now.
2026-05-27 20:23:23 +02:00
mAi
0568d340a7 feat(procedures): U1 fold Mode A (Direkt suchen) (m/paliad#151)
Mounts mountModeA() into #procedures-panel-search when the Direkt-suchen
tab activates. The legacy fristenrechner-mode-a code runs unchanged
inside a wrapper that reseeds the #fristen-overhaul-root /
#fristen-overhaul-mode-host scaffold on every tab activation, so
re-clicking the tab always restores a fresh Mode A surface even if the
previous interaction committed an event into the result view.

`?event=<code>` deep links still resolve: boot detects the param,
activates the search tab, and hands directly to mountResultView() —
the result lands inside the same root, the user sees the picked
event's follow-up rules with the Direkt-suchen tab as the visible
context.

Search-box-in-filter-strip composition with chip filters (m's Q3
divergence) lands later, after Mode B + Verfahrensablauf are folded —
the unified state machine pulls all three behind one search input.
2026-05-27 20:21:39 +02:00
mAi
60907e7153 feat(procedures): U0 skeleton — /tools/procedures page shell (m/paliad#151)
First slice of the unified procedural-events tool train. Ships only the
page chrome — route, sidebar/header, filter strip with search box, four
entry-mode tabs (Verfahren wählen / Direkt suchen / Geführt / Aus Akte),
and the host containers later slices mount their UI into. No data wiring.

Per m's decisions (design §11.5): URL is English (/tools/procedures, not
/tools/verfahren); all four tabs visible from boot (not a single-default
landing); search box lives in the top filter strip and will compose with
chip filters once U1+ wire them.

U1 fills #procedures-panel-search (Mode A), U2 fills -wizard (Mode B),
U3 fills -proceeding + #procedures-output-tree (Verfahrensablauf), U4
hard-cuts /tools/fristenrechner and /tools/verfahrensablauf to 301
redirects and drops the legacy pages.
2026-05-27 20:19:15 +02:00
mAi
66b08813c4 Merge: t-paliad-334 — unified procedural-events tool design (m/paliad#151)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
cronus shipped 568-line design ratifying a single /tools/procedures page that folds Fristenrechner (Mode A + Mode B + result view) + /tools/verfahrensablauf into one surface with 4 entry tabs and tree+linear-drawer outputs.

12 m's decisions (9 on-recommendation, 3 divergent):
- Q (divergent): English URL '/tools/procedures' (over '/tools/verfahren')
- Q (divergent): all 4 entry tabs visible + search-in-filter-strip (over single default tab)
- Q (divergent): hard-cut 301 redirect (over 2-week dual-ship)

Stays separate (correctly different shape/audience):
- /admin/procedural-events (editorial write surface)
- /projects/{id} Verlauf (per-Akte actuals)
- SmartTimeline (internal projection)
- youpc.org/deadlines (cross-repo, embedded snapshot)

5-slice migration train U0-U4 (no DB mig — purely UX consolidation atop the shipped Phase 2 substrate).

Inventor parks. Head dispatches Sonnet coder per project memory directive (NOT cronus for impl).
2026-05-27 19:56:04 +02:00
mAi
0aaa523494 design: fold m's 12 decisions into unified procedural-events doc (m/paliad#151)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
- §11.5 m's decisions section (9 on-rec + 3 divergent)
- diverged: Q2 /tools/procedures (English), Q3 all-tabs+search-in-filter-strip, Q11 hard cut no dual ship
- §11.5.1 changes triggered by divergences (URL rename, all-tabs behaviour, U4 rewrite)
- URL refs throughout body updated to /tools/procedures
- U4 slice rewritten to 301 hard-cut per Q11
2026-05-27 19:54:50 +02:00
mAi
d49ff55c41 design: unified procedural-events tool (m/paliad#151 shift-1, draft)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
- audit of 6 surfaces with question→dimension matrix
- proposal: fold Fristenrechner + Verfahrensablauf into /tools/verfahren
- 4 entry paths converge on tree + linear output shapes
- mobile narrow-viewport rules + 3 worked personas
- 5-slice migration train (U0-U4), no DB migration
- 12 open questions in 3 batches for AskUserQuestion
2026-05-27 19:23:24 +02:00
mAi
ae1c0b861d Merge: fix admin-rules-edit URL parser regex (post B.6 rename hotfix)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
2026-05-27 17:58:49 +02:00
mAi
c8999e2a8b fix(admin-rules-edit): accept /admin/procedural-events/{id}/edit in URL parser
Slice B.6 / S6 renamed the canonical edit URL from /admin/rules/{id}/edit
to /admin/procedural-events/{id}/edit. The backend handler + 301 redirect
landed, but the client-side regex in admin-rules-edit.ts:110 was missed —
it still only matches the legacy /admin/rules/.../edit shape. Result:
visiting the canonical URL from the list page shows 'Ungültige
Verfahrensschritt-ID in der URL.' even though the rule exists.

Fix: regex accepts both '/admin/procedural-events/{id}/edit' (canonical)
and '/admin/rules/{id}/edit' (legacy, kept for stale tabs / bookmarks
during the deprecation window).

m flagged 2026-05-27 17:57 on rule cc439590 (RoP.262.2, upc.inf.cfi).
2026-05-27 17:58:49 +02:00
mAi
0365e84dd1 Merge: t-paliad-331 P2 + P4 partial — condition_expr validator + trigger_events partial deprecation (m/paliad#149)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
ritchie shipped the final two slices of the Phase 2 train.

P2 — condition_expr write-validator:
- New internal/services/condition_expr_validator.go (136 LoC) — locks the grammar to {flag:<str>} OR {op:'and'|'or', args:[<leaf>|<composite>]} per design §4.1
- RuleEditorService.Create + Update reject non-conforming expressions
- 166-LoC test coverage; all 18 existing condition_expr rows validate

P4 (partial) — trigger_events deprecation (mig 156):
- NULLs out the 2 hybrid rules' trigger_event_id (parent_id is the canonical edge per §2.1)
- Adds 'Deprecated: see m/paliad#149' header on the legacy /api/tools/event-deadlines route
- Does NOT drop paliad.trigger_events nor the 5 read sites — those are gated on the editorial reparenting of the 73 orphan globals (NULL proceeding_type_id, served only via trigger_event_id). Editorial work is m's, not coder scope.

Comment on m/paliad#149 (issuecomment-10436) enumerates the exact next steps for the eventual follow-up coder once editorial reparenting completes.

Phase 2 train: P0 + S1+S1a + P1 + P3 + P2 + P4 partial — ALL shipped. Final P4 step waits on editorial.
2026-05-27 15:27:12 +02:00
mAi
d6a5dedb2b feat(deadline-system): P4 (partial) — partial trigger_events deprecation (m/paliad#149)
Phase 2 P4 partial-scope (head approved 2026-05-27 15:24). The full
drop of paliad.trigger_events + the legacy route + 5 read sites is
gated on an editorial backfill that's not in coder scope — 73 active
sequencing_rules carry proceeding_type_id IS NULL and are addressed
ONLY via trigger_event_id today. Dropping anything would break those
73 orphans.

What this lands:

  1. Mig 156 — NULL out trigger_event_id on the 2 hybrid rules that
     carry BOTH parent_id AND trigger_event_id. Per design §2.1 /
     m's Q1, parent_id is the canonical predecessor link; the
     hybrid trigger_event_id was redundant. The 2 rules' parent_id
     chains keep the live edge. Live-DB verified post-apply: 0
     active hybrid rules remain.

  2. Deprecation + Link headers on POST /api/tools/event-deadlines
     per RFC 8594 / RFC 9745. The route stays functional so the 73
     orphans keep working until reparenting lands.

What this does NOT land (gated on editorial):

  - DROP TABLE paliad.trigger_events
  - DROP COLUMN paliad.sequencing_rules.trigger_event_id
  - Remove the legacy /api/tools/event-deadlines handler
  - Remove EventDeadlineService + ExportService::1680 sheet
  - Remove deadline_rule_service.go:226 label-fallback path
  - Remove event_type_service.go:40+414 reads (33 event_types still
    reference trigger_event_id)
  - Update cmd/gen-upc-snapshot/main.go:185-202 to skip trigger_events
  - Drop the sequencing_rules_trigger_event_id_fkey FK

All of the above lands in a follow-up mig once the orphan count
hits zero. Comment to follow on m/paliad#149 with the editorial-
backlog list.

Verified: live-DB pre/post hybrid count (0 active hybrids remain);
mig idempotent; go vet clean.

Design: docs/design-deadline-system-revision-2026-05-27.md §2.1
(parent_id canonical), §3.4 (legacy route fate), §4.3 (table fate),
§5 (slice train P5 row). t-paliad-331.
2026-05-27 15:25:53 +02:00
mAi
9940dd8216 feat(deadline-system): P2 — condition_expr write-validator (m/paliad#149)
Phase 2 P2 (design §4.1). Locks the condition_expr grammar to:

  CondExpr := { "flag": "<known_flag>" }
            | { "op": "and"|"or", "args": [<CondExpr>, ...] }

Where <known_flag> must exist in paliad.scenario_flag_catalog (today:
with_ccr / with_amend / with_cci; editorial adds via the catalog
table as needed).

Wire-time validation in RuleEditorService.Create and UpdateDraft —
the rule editor surfaces a 400 with a friendly message before the row
hits the DB. Empty / JSON null inputs pass through (the "no gate"
shape; stored as NULL column).

The validator:
  * walks the JSON tree once, collecting every leaf flag name
  * rejects mutually-exclusive shapes (leaf + composite in one node)
  * rejects empty args, bad op values, empty flag strings
  * does ONE batch lookup of the collected leaf names against the
    catalog (regardless of expression depth)

Tests:
  * 9 shape-only unit tests covering every reject path (no DB needed)
  * TestValidateConditionExpr_LiveCatalog covers 6 good shapes + 2
    unknown-flag cases against the live catalog
  * TestConditionExpr_AllLiveRowsValidate runs the validator over
    every active+published condition_expr in paliad.sequencing_rules
    to enforce the §4.1 invariant on every deploy (today's 18 rows
    all conform — verified via Supabase MCP pre-flight)

Live-DB tests skip cleanly when TEST_DATABASE_URL is unset (same
posture as sibling live tests in this package).

Design: docs/design-deadline-system-revision-2026-05-27.md §4.1
(grammar formalisation). t-paliad-331.
2026-05-27 15:22:53 +02:00
mAi
f6add95d0a Merge: t-paliad-331 P3 — Verfahrensablauf three-way detail filter (m/paliad#149)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
ritchie shipped m's headline UX (paliadin priority signal 14:58):

  'The new timeline filters for optional / mandatory / show only selected
   is what I am most waiting for. I want this to be consolidated for all
   our deadlines so we can simulate all proceedings.'

Three-way detail-level filter above the Verfahrensablauf result panel:
- Nur Pflicht — only priority='mandatory' rules
- Gewählt (default) — mandatory + recommended + every explicit per-rule override in projects.scenario_flags
- Alle Optionen — every rule, unselected ones rendered dotted-border + muted

State persists per-user via localStorage['verfahrensablauf:view_mode']. Per-rule Aufnehmen/Entfernen chips wire to projects.scenario_flags via the P0 SSoT (rule:<uuid> entries).

New files: verfahrensablauf-detail-mode.ts (125), verfahrensablauf-detail-mode.test.ts (96), filter wiring in verfahrensablauf.ts (+204) and views/verfahrensablauf-core.ts (+37). 63 LoC CSS (dotted-border treatment).

bun build clean, 264 frontend tests pass (8 new), go vet clean.

Ritchie continuing with P2 (condition_expr write-validator) then P4 (legacy deprecation).
2026-05-27 15:20:58 +02:00
mAi
480332a5f5 feat(deadline-system): P3 — three-way detail filter on Verfahrensablauf (m/paliad#149)
m's headline UX ask (2026-05-27 14:58, paliadin priority signal):

  "The new timeline filters for optional / mandatory / show only
   selected is what I am most waiting for. I want this to be
   consolidated for all our deadlines so we can simulate all
   proceedings."

Phase 2 P3. Adds a three-way detail-level filter above the result
panel on /tools/verfahrensablauf:

  ( ) Nur Pflicht     — only priority='mandatory' rules
  (•) Gewählt         — mandatory + recommended (default) + every
                         explicit per-rule override the user has set
                         in projects.scenario_flags
  ( ) Alle Optionen   — every rule, with unselected ones rendered
                         dotted-border + muted so the user sees what
                         they're NOT considering

State persists per-user via localStorage["verfahrensablauf:view_mode"].
The filter is pure client-side narrowing on the calc payload — flipping
the toggle re-renders instantly without a fresh backend call.

Per-rule selection (design §2.4a): every optional / recommended card
now carries an [Aufnehmen] / [Entfernen] chip. Clicking writes a
"rule:<uuid>" entry into the project's scenario_flags via the P0 SSoT
PATCH endpoint, recording only deviations from the priority default:

  recommended + entfernen → rule:<uuid> = false  (explicit deselect)
  optional    + aufnehmen → rule:<uuid> = true   (explicit select)
  flipping back to the default deletes the entry

Mandatory rules never expose the chip — they cannot be deselected.

Wire-shape change: CalculatedDeadline gains `ruleId` (the backend already
emits it as `ruleId` in TimelineEntry; only the frontend interface needed
to surface it).

Conditional handling: a conditional rule whose predicate doesn't fire
is treated as unselected in "Gewählt" mode (even when priority=
mandatory) — mandatory means "must be filed IF the predicate fires",
not "always render". The "Alle Optionen" view re-surfaces it so the
lawyer can see what scenario would unlock it.

Cross-surface coherence: hydrating ?project=<id> reads scenario_flags
from the SSoT and pre-fills the existing flag checkboxes (with_ccr /
with_amend / with_cci) so the page reflects the project's persisted
state on first paint. Every flag toggle + every per-rule chip click
PATCHes back. The page also listens for the scenario-flag-changed
CustomEvent fired by peer surfaces (Mode B Fristenrechner result-view)
and re-renders without a fresh fetch.

i18n: 5 new keys (deadlines.detail.{label,mandatory_only,selected,
all_options,optional_unselected_hint,aufnehmen,entfernen}) DE + EN.

CSS: dotted-border + muted treatment on .timeline-item-header--
unselected; .timeline-selection-chip with --add (lime accent) and
--remove (discreet muted) variants.

Tests: 8 new unit tests covering isRuleSelected (4 priority × 2 flag
state matrix) and filterByDetailMode (3 modes × default/override cases).

Verified: bun build clean, bun test 264/264 (8 new), go vet clean.

Design: docs/design-deadline-system-revision-2026-05-27.md §2.4a
(selection state model), §3.3a (view-mode toggle), §6 (Entry A spec).
t-paliad-331. Re-prioritised by m via paliadin 14:58.
2026-05-27 15:20:07 +02:00
mAi
97d90ce651 Merge: t-paliad-331 — Phase 2 slices P0 + S1+S1a + P1 (m/paliad#149)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
ritchie shipped three slices of the Phase 2 deadline-system revision train per design §5:

P0 — Scenario SSoT (mig 154):
- ALTER TABLE projects ADD COLUMN scenario_flags jsonb DEFAULT '{}'
- New paliad.scenario_flag_catalog table
- GET/PATCH /api/projects/{id}/scenario-flags endpoints
- Verfahrensablauf + result-view checkbox binding read+write through scenario_flags
- Per-rule selection state via 'rule:<uuid>' entries (generalises the with_ccr dropdown pattern, no new column on sequencing_rules)
- New scenario_flags_service.go (375 LoC), scenario-flags.ts client, i18n keys

S1+S1a — Cross-party display + spawn-only picker filter:
- FristenrechnerService.LookupFollowUps stops filtering by party server-side; returns all + primary_party
- UI groups: own-side checked-by-priority, cross-party annotated 'Gegenseitig' badge + unchecked
- SearchEvents SQL adds AND sr.is_spawn = false to filter spawn-only events as triggers
- lookup_events_test.go regression coverage

P1 — upc.apl re-split (m's Q5 divergence, mig 155):
- Reverts upc.apl.unified (id=160) back into upc.apl.merits / upc.apl.cost / upc.apl.order split
- Retargets 16 sequencing_rules to the appropriate split id
- Mig 155 applied to live DB per ritchie's report

Next per m's 14:58 priority signal: P3 (Entry A Verfahrensablauf tree UI with three-way view-mode toggle) — ritchie jumping straight there, then P2 + P4.
2026-05-27 15:12:52 +02:00
mAi
3a4e99cb92 feat(deadline-system): P1 — upc.apl re-split into merits/cost/order (m/paliad#149)
Phase 2 P1 / m's Q5 divergence (2026-05-27, verbatim):

  "Reverse the unification as suggested in 3. They are different
   proceedings, I only wanted the approach to be unified in the
   'determinator' — but they are actually different proceedings!"

Mig 155 reverts the mig-096 unification:

  Before: id=160 upc.apl.unified active (16 rules), id=11/19/20 inactive
  After:  id=11 upc.apl.merits (7 rules), id=19 upc.apl.cost (2 rules),
          id=20 upc.apl.order (7 rules) all active; id=160 inactive

The 16 rules under id=160 split cleanly by event_code prefix; all 10
parent_id edges among them are bucket-local (pre-flight audit), so
the tree shape survives the rebind unchanged.

Spawn FK retarget: pi.cfi.appeal_spawn flips from 11 (merits) → 20
(orders track) per design §3.1 — PI appeals land on orders, not
merits. The inf/rev/dmgs spawns keep target=11 (merits), now active.

Determinator routing layer (proceeding_mapping.go) keeps its single
"Berufung" front door per m's intent — only the data shape changes.

Pre-flight verified: 0 projects bound to id=160, 0 scenarios reference
upc.apl. Zero data migration on the project side.

Tests: lookup_events_test.go assertions on the three appeal_target
buckets updated to the new codes (endentscheidung → upc.apl.merits,
schadensbemessung → upc.apl.merits, bucheinsicht → upc.apl.order).
Same rule set, post-split coordinates.

Snapshot regen (pkg/litigationplanner/embedded/upc/) deferred: the
current snapshot only contains inf+rev so the apl re-split doesn't
shift its contents; regenerating would surface unrelated active PTs
and pollute this slice. Tracked as a follow-up.

Verified: go vet clean, go test ./internal/services/... -run
LookupEvents|proceeding_codes clean.

Design: docs/design-deadline-system-revision-2026-05-27.md §3.1
(re-split mig), §1.3 (spawn graph post-Q5). t-paliad-331.
2026-05-27 15:11:48 +02:00
mAi
3533d79a25 feat(deadline-system): S1+S1a — cross-party display + spawn-only picker (m/paliad#149)
Phase 2 S1 + S1a (pre-ratified from t-paliad-327, folded into the
Phase 2 train).

S1 — Cross-party display:
- FristenrechnerService.LookupFollowUps stops filtering by party
  server-side; queryFollowUpRows drops the perspective WHERE clause
  and returns every published+active child.
- Server now computes is_cross_party per row (true only when
  perspective ∈ {claimant,defendant} AND primary_party is the
  opposite side; NULL/both/court is never cross-party).
- FollowUpRule wire shape gains the boolean.
- Frontend renderRule adds a "Gegenseitig" badge + is-cross-party
  row class (muted styling, disabled checkbox affordance).
- defaultChecked returns false for cross-party rows.
- countSelected + submitWriteBack skip cross-party rows
  unconditionally — even if a user manually checks the box, they
  describe opposing-side filings and don't belong in our Akte set
  (design §2.4 write-back exclusion).
- i18n: deadlines.overhaul.crossparty.badge / .tooltip (DE+EN).
- CSS: .fristen-overhaul-rule-crossparty + .is-cross-party row
  modifier.

S1a — Spawn-only picker filter:
- SearchEvents WHERE now adds `sr.is_spawn = false` so spawn rules
  (e.g. appeal_spawn, the inf.cfi → upc.apl.merits hop) no longer
  surface as picker hits. Spawn rules are consequences, not
  triggers — a lawyer searching "Berufung" wants the appeal-tree
  root, not the inf.cfi spawn link.
- Terminal leaves (Duplik etc.) stay pickable per design §2.2's
  carve-out: their own anchor is non-spawn, so they surface and
  render an honest empty follow-up list.

Honest UX: hiding cross-party follow-ups lied about what the
workflow does next (cf. RoP.029.d falling off when perspective=
claimant on def_to_ccr — the workflow continues, just on the
defendant's docket). The fix makes the data legible without
contaminating the write-back path.

Verified: go vet clean, bun build clean, bun test 256/256,
go test ./internal/services/... -run LookupFollowUps... clean.

Design: docs/design-deadline-system-revision-2026-05-27.md §2.4
(cross-party) + §2.2 (spawn-only picker). t-paliad-331.
2026-05-27 15:07:01 +02:00
mAi
2a69f7fc6c Merge: t-paliad-332 — UPC vacations no longer block deadlines (align with paliad t-paliad-121)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
brunel aligned the embedded snapshot calendar with paliad-side policy from t-paliad-121: UPC vacation rows stay in the data for informational annotation but no longer trigger IsNonWorkingDay → AdjustForNonWorkingDays leaves dates intact.

Changes:
- pkg/litigationplanner/embedded/upc/holidays.go: IsNonWorkingDay now returns true only on closure (not vacation). Vacations still surface via AdjustForNonWorkingDaysWithReason for labelling.
- pkg/litigationplanner/embedded/upc/holidays.json: regenerated from live paliad.holidays. Was 5 placeholders → now 55 holidays (33 vacation + 22 closures). Includes UPC Winter Vacation rows.
- pkg/litigationplanner/embedded/upc/meta.json: snapshot version bump.
- snapshot_test.go: +42 lines covering the vacation non-blocking behavior with regression cases.

Affects youpc.org/deadlines (consumes pkg/litigationplanner via Go module replace) — picks up automatically on rebuild.
2026-05-27 15:05:06 +02:00
mAi
39353d49ed fix(litigationplanner): UPC vacations no longer block deadlines (align with paliad t-paliad-121)
youpc.org/deadlines was rolling a deadline "from 2027-01-02 (UPC Winter
Vacation)" — i.e. across the UPC judicial vacation as if it were a
public holiday. Paliad-side t-paliad-121 already decided vacations are
informational only (the Court keeps running through them, RoP / UPC AC
decision-on-judicial-vacation 2023-05-26), and `HolidayService.Is
NonWorkingDay` in `internal/services/holidays.go` is correct. The
embedded snapshot consumed by youpc.org via Go-module replace had
drifted: `pkg/litigationplanner/embedded/upc/holidays.go:74` blocked on
both `isClosure()` AND `isVacation()`.

This commit aligns the embedded calendar with the paliad-side semantics
and ships a fresh holiday set so the existing 2026/2027 fix actually
takes effect downstream.

Code changes (`holidays.go`):

- `IsNonWorkingDay`: drop the `|| h.isVacation()` branch — only weekends
  and `isClosure()` rows trigger the roll. Godoc rewritten to mirror
  the paliad-side rationale (Court keeps operating, RoP cites,
  vacation rows kept for informational labels).
- `isClosure()`: accept both `"public_holiday"` and `"closure"`. Live
  paliad DB rows use the `public_holiday` value; the placeholder
  snapshot shipped with the original Slice C used `closure` as a
  hand-crafted synonym. Reconciles with
  `internal/services/holidays.go:132` which already does the same
  union. Required to make the regenerated JSON (full of
  `public_holiday`) keep blocking DE national holidays after the
  regeneration in this commit.
- Type-level godoc updated: `SnapshotHolidayCalendar` now documents
  vacation-is-informational, and the `AdjustForNonWorkingDaysWithReason`
  precedence note explains that `vacation` kind only fires when a
  vacation row overlaps a weekend or closure that's already doing the
  rolling.

Data refresh (`holidays.json`):

- Regenerated from paliad prod (postgres @ 100.99.98.201:11833,
  paliad schema). 55 rows for 2026 + 2027: 22 DE public_holiday +
  33 UPC vacation (25 Summer Vacation Jul 27–Aug 28, 8 Winter
  Vacation Dec 24/28–31 + Jan 4–6). The previous placeholder shipped
  only 5 rows (3 Sommerpause + Neujahr + Tag der Arbeit, no Winter
  Vacation at all) — which is why a date landing in late Dec / early
  Jan landed inside an unmodeled gap on the consumer side.
- `meta.json` bumped: version → `2026-05-27-1-holidays-only`,
  `holiday_count` 5 → 55, `source_db_label` flags that only
  holidays.json was refreshed (see friction note below).

Regression test (`snapshot_test.go::TestSnapshotHolidayCalendar`):

- 2026-08-04 (Tue, UPC Summer Vacation) — `IsNonWorkingDay` must be
  false; `AdjustForNonWorkingDays` must NOT mutate the date.
- 2027-01-02 (Sat, m's flagged scenario) — must roll forward through
  Sat/Sun, then STOP on Mon 2027-01-04 (UPC Winter Vacation, no longer
  blocking). Pre-fix this rolled all the way to Thu 2027-01-07.

Cross-repo: youpc.org imports `pkg/litigationplanner` via Go-module
replace; the regenerated snapshot ships on its next rebuild. No
separate youpc.org commit needed — paliad is the source of truth.

Friction note: `cmd/gen-upc-snapshot/main.go` itself is incompatible
with the current paliad schema. Migration 140 (`140_drop_deadline_rules`)
dropped `paliad.deadline_rules`, but the generator still SELECTs from
it (main.go ~L162). Running the tool against prod fails on the rules
step. I bypassed the broken path and generated `holidays.json` directly
from the DB via psql + jq (same JSON shape that `EmbeddedHoliday`
expects, nulls filtered for `omitempty`). The other snapshot files
(rules.json, proceeding_types.json, trigger_events.json, courts.json)
remain at their pre-existing placeholder state — re-flagged in
meta.json's `source_db_label`. Refitting the generator for the post-
mig-140 schema is a separate task.

go vet + go test ./... clean (256+ Go tests pass, including the new
regression cases).
2026-05-27 15:04:05 +02:00
mAi
d36cc9ee15 feat(deadline-system): P0 — per-project scenario_flags SSoT (m/paliad#149)
Phase 2 P0 of the deadline + procedural-events revision. Establishes
paliad.projects.scenario_flags (jsonb) + paliad.scenario_flag_catalog as
the single source of truth for per-project scenario state — replacing
the three fragmented stores athena flagged (project_event_choices,
scenarios.spec, DOM-only). All three were empty per the audit so no
data migration is needed.

The jsonb map carries two key shapes:

  * named flags (whitelist via scenario_flag_catalog) — today
    with_ccr / with_amend / with_cci
  * per-rule selection deviations of shape "rule:<uuid>" — wired up
    here for validation; the consumer UI lands in P3

Endpoints:

  GET   /api/projects/{id}/scenario-flags
  PATCH /api/projects/{id}/scenario-flags

PATCH semantics: bool = write; null = delete (priority-driven default
returns); missing key = leave alone. The service validates every key
on write (catalog lookup + UUID rule-membership + mandatory-cannot-be-
deselected) before persisting, so a single bad key fails the whole
patch.

Frontend bind: new scenario-flags.ts client module + Mode B's flag
checkboxes (ccr-flag / inf-amend-flag / rev-amend-flag / rev-cci-flag)
now hydrate from / persist to the project's scenario_flags on every
toggle. Kontextfrei (no project) is unchanged. Cross-surface coherence
via a scenario-flag-changed CustomEvent (peer surfaces — Verfahrens-
ablauf strip, Mode B result-view — will subscribe in P3).

Mig 154 is audit-defensive (set_config of paliad.audit_reason); no
audit trigger fires on paliad.projects today but a future one will
inherit the reason. Seeds the three known flags. CHECK constraints
enforce the top-level shape (jsonb_typeof = 'object') and the
catalog key pattern (lowercase, not 'rule:%' prefix).

Verified against the live DB: 18 projects default to '{}', catalog
has 3 rows, applied_migrations advanced to 154.

Design: docs/design-deadline-system-revision-2026-05-27.md §2.3, §2.4a,
§4.1, §5 (P0 row). t-paliad-331.
2026-05-27 15:02:01 +02:00
mAi
a9fd979cdb Merge: t-paliad-330 — timeline view dark-mode CSS token migration
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
brunel converted hardcoded hex to existing design tokens for the .timeline-* classes (Verfahrensablauf at ?view=timeline). No new tokens introduced; 11 existing tokens reused. Layout/spacing untouched.

Mirrors the t-paliad-326 playbook applied earlier to the Fristenrechner overhaul CSS, but on the separate timeline view that wasn't in scope then.
2026-05-27 14:52:35 +02:00
mAi
c48fa93e3d fix(verfahrensablauf): dark-mode token migration for timeline view CSS
The /tools/verfahrensablauf?view=timeline page (and the columns-view
mirror via `.fr-col-item--*` modifiers) had hardcoded light backgrounds
that survived a dark-mode flip. Root cause: four undefined custom
properties (`--color-bg-soft`, `--color-border-soft`, `--color-accent-bg`,
`--brand-lime`) consumed by the `.timeline-*` and adjacent
`.event-card-choices-*` rules, each with a hardcoded hex / RGBA fallback.
Since neither :root nor :root[data-theme='dark'] defines those tokens,
every consumer fell through to the light fallback in both themes —
leaving the conditional-row bg, the popover surface, the option-button
bg, the chip-skipped bg, and the popover block-separator border stuck on
near-white in dark mode. Earlier t-paliad-326 covered the new
Fristenrechner overhaul CSS only; the timeline view styles are a
separate block (~L3337-3810 in `frontend/src/styles/global.css`) and
were not touched.

Migrate every consumer to the existing dual-theme tokens already used
across paliad. Same approach m approved for t-paliad-326, no new tokens
introduced (all reuse):

- Card / popover surfaces (`.event-card-choices-popover`,
  `.event-card-choices-option`) → `--color-surface` (white light /
  card-tint dark) so the popover reads as raised above the body in
  both themes.
- Subtle raised surface for conditional row, skipped chip, option
  hover (`.timeline-item--conditional .timeline-content`,
  `.fr-col-item--conditional`, `.event-card-choices-chip-part--skipped`,
  `.event-card-choices-option:hover/:focus-visible`) →
  `--color-surface-muted`. **This is the visible bug fix m flagged.**
- Lime-tint backdrops (`.timeline-context-note` bg,
  `.event-card-choices-caret:hover/:focus-visible`) →
  `--color-bg-lime-tint` (lime-alpha 0.10 light / 0.12 dark).
- Active-option chip bg (`.event-card-choices-chip-part`) →
  `--color-accent-soft-bg`.
- Lime accent borders / fills (`.timeline-context-note` left border,
  `.timeline-date--overridden`, `.frist-date-edit-input`,
  `.event-card-choices-unhide-btn`, `.event-card-choices-option--active`)
  → `--color-accent` / `--color-accent-fg`. Drops the
  legacy `#c6f41c` fallback that doesn't match the current brand
  (`--hlc-lime: #BFF355`).
- Neutral borders (`.event-card-choices-caret`,
  `.timeline-item--hidden .timeline-content`, `.fr-col-item--hidden`,
  `.timeline-item--conditional .timeline-content`,
  `.event-card-choices-popover`, `.event-card-choices-option`,
  `.event-card-choices-block + .event-card-choices-block`) →
  `--color-border` (warm cream-derived light / cream-alpha dark).
- Popover shadow `rgba(0, 0, 0, 0.12)` → `var(--shadow-md)`
  (auto-deepens to `rgba(0, 0, 0, 0.45)` in dark).
- Status red (`.event-card-choices-error`) → `--status-red-fg`
  (defined in both themes; old `#b00020` fallback unreachable).

Zero new tokens introduced — every replacement uses a token already
shipped in both :root and :root[data-theme='dark']. Verified by
mounting `frontend/dist/assets/global.css` against a representative
static DOM (context-note banner, every timeline-item variant —
conditional / skipped / hidden / overridden-date / mandatory —
popover with active/inactive options, unhide button, error message,
all four party-badge stances) and toggling `data-theme` between
light and dark: conditional row bg flips from grey to muted-cream-on-
midnight, popover lifts off the body via `--shadow-md`, every chip and
border stays legible in both themes.

bun build + bun test (256 pass) + go vet clean.
2026-05-27 14:51:15 +02:00
mAi
5f7a66bbec Merge: t-paliad-329 — Phase 2 deadline + procedural-events full revision design (m/paliad#149)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
atlas shipped the comprehensive Phase 2 design covering Option B (full revision absorbs t-paliad-327 narrow scope). 776 lines.

Spine: connection schema for all procedural events as ASCII trees per PT + Mermaid spawn graph. parent_id canonical, trigger_event_id deprecated.

Tier 1 (4 questions, all on-recommendation):
- parent_id canonical predecessor link
- Trigger discoverability via derived EXISTS check
- Scenario state SSoT in projects.scenario_flags jsonb (mig 154)
- Cross-party display contract

Tier 2 (4 questions, 1 divergent):
- Q5 m diverged: revert upc.apl.unified back into merits/cost/order split (mig P1 retargets 16 rules)
- Spawn-only events excluded from picker
- Entry A 'sequence-from-proceeding-type' view extends /tools/verfahrensablauf
- Legacy /api/tools/event-deadlines + paliad.trigger_events deprecated

Tier 3: condition_expr grammar formalised ({flag} / {op:and|or,args:[...]}), editorial backfill workflow on /admin/procedural-events parent-NULL filter, trigger_events table dropped.

Shift-2 additions (per m's 14:31-14:35 direction, folded in 490c8a8):
- Selection state via per-rule scenario_flags entries (rule:<uuid>) — generalises the existing with_ccr dropdown pattern. NO new column on sequencing_rules.
- Three-way view-mode toggle on Verfahrensablauf: Nur Pflicht / Gewählt / Alle Optionen.
- R.109 chain (Antrag auf Simultanübersetzung → Mitteilung Dolmetscherkosten) as Tier 3 editorial worked example: current parent_id linkage is wrong; right shape uses with_interpreter_denied flag.

6-slice migration train:
- P0 Scenario SSoT (mig 154 + endpoints + binding)
- S1+S1a Cross-party display + spawn-only picker filter
- P1 upc.apl re-split (16 rules retarget)
- P2 condition_expr write-validator
- P3 Entry A Verfahrensablauf tree UI
- P4 Legacy deprecation + trigger_events drop

Worked examples for both entry paths. Coder dispatch awaits m's go.
2026-05-27 14:45:43 +02:00
mAi
490c8a8c8c design(deadline-system): fold m's selection + view-mode + R.109 additions (t-paliad-329)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
m's clarification at 14:40 reframed the original "rarity" framing: every
optional rule is a per-scenario selectable card; the Verfahrensablauf
gets a three-way detail-level filter (Nur Pflicht / Gewählt / Alle
Optionen). The CCR-dropdown pattern generalises to per-rule chips.

Three folded additions:

§2.4a — Selection state + detail-level filter. NO new column on
sequencing_rules (reverts the earlier is_edge_case strawman). Extends
projects.scenario_flags jsonb to carry both named flags (with_ccr etc.)
and per-rule entries (rule:<uuid>). Storage only carries deviations
from the priority default (recommended = default-selected,
optional = default-unselected). Whitelist accepts rule:<uuid> when the
UUID resolves to an active rule on the project's PT.

§3.3a — Verfahrensablauf view-mode toggle: three-way segmented control,
localStorage persistence, default "Gewählt". Mode B result view stays
single-purpose (no view-mode toggle there).

§4.2.1 — R.109 translation chain editorial worked example: R.109.1 stays
as optional anchor; R.109.4 reparents to R.109.1 with condition_expr
{flag: with_interpreter_denied} and primary_party=both (parties, not
court); R.109.5 reparents to R.109.1 with {flag: with_translation_granted}.
Introduces two new flags to scenario_flag_catalog.

§6 UI spec updated: two mocked tree states (Gewählt + Alle Optionen)
showing the dotted-border [Aufnehmen] chips, [Entfernen] on selected
optionals, greyed-with-hint on flag-gated conditionals, and the
subtree-hide-on-unselected-ancestor render logic.

§10.0a captures the additions; §10.1 notes they don't change the slice
train (P0 + P3 take the extended scope; no new mig).
2026-05-27 14:45:00 +02:00
mAi
b1c9e8dd97 design(deadline-system): Phase 2 revision — connection schema + 12 m's decisions (t-paliad-329)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
Builds on athena's Phase 1 assessment (9aee9e4) + atlas's t-paliad-327
pre-ratified subset. m's Option B direction: "overall schema for all
procedural events and how they are connected" — connection graph as the
spine.

Connection schema (§1):
- Rules are nodes, parent_id is the canonical edge, spawn rules are the
  cross-PT edges, condition_expr filters the visible subgraph
- ASCII trees for the 3 largest PTs (upc.inf.cfi 25, upc.rev.cfi 17,
  upc.apl post-Q5-split 16); Mermaid graph for the 4 spawn cross-PT edges
- Per-PT health table covering all 23 active primaries (17 ruled + 6 empty)

m's 12 design decisions (3 batches of 4 via AskUserQuestion):

Tier 1 — model (all 4 on-recommendation):
- Q1: parent_id is canonical, deprecate trigger_event_id
- Q2: Reparent 73 legacy globals via editorial walk
- Q3: Derive trigger discoverability from data (EXISTS)
- Q4: projects.scenario_flags jsonb (confirms t-paliad-327 design)

Tier 2 — surface (1 divergent, 3 on-recommendation):
- Q5 DIVERGENT: Reverse the upc.apl unification — split back into 3 PTs
  (merits/cost/order). m: "I only wanted the approach to be unified in
  the 'determinator' — but they are actually different proceedings!"
  Mig P1 retargets 16 rules by event_code prefix.
- Q6: Show empty PTs with "Keine Regeln gepflegt" badge
- Q7: Fold Entry A into /tools/verfahrensablauf
- Q8: Drop /event-deadlines after 73-globals reparenting

Tier 3 — editorial (all on-recommendation):
- Q9: Lock condition_expr grammar {flag} | {op:and|or, args}
- Q10: parent-NULL filter on /admin/procedural-events
- Q11: Drop trigger_events table once route is gone
- Q12: ASCII per-PT + Mermaid spawn graph

6-slice migration train (§5): P0 scenario SSoT, P1 appeal re-split, S1/S1a
from t-paliad-327, P2 empty-PT badge, P3 Entry A, P4 editorial walk, P5
trigger_event_id deprecation. P5 gated on P4.

No code yet — coder gate held per inventor SKILL.
2026-05-27 14:32:02 +02:00
mAi
9aee9e4101 Merge: t-paliad-328 — Phase 1 assessment of the deadline + procedural-events system (m/paliad#149)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled
athena delivered the consultant audit per RFC m/paliad#149 Phase 1:

- 738-line doc, read-only, no design proposals
- §1 consumer audit: every service / handler / frontend / migration that touches sequencing_rules or procedural_events, cited file:line
- §2 health-check: green / yellow / red / dead-code buckets
- §3 corpus quality: parent_id 47% coverage, condition_expr keys, spawn distribution, primary_party by PT, court-set, trigger_event_id overlap
- §4 editorial gap map per proceeding_type
- §5 risk register: 11 items, severities marked
- §6 recommendation: Tier 1 model decisions to grill first, Tier 2 surface decisions, Tier 3 editorial+cleanup

Headline risks bumped from the RFC's framing:
- R1 cross-party filter (high) — 39 rules dropped
- R2 picker over-accepts (high) — only 67/222 events are real chain-anchors
- R3 4 spawn rules target inactive proceeding_type id=11 (high) — dangling FK
- R4 73 legacy globals with NULL PT (medium) — invisible to Mode B
- R5 5 surfaces still read legacy trigger_events bigint table (medium)
- R6 3 scenario stores, all empty but all live (medium) — clarified: paliad.scenarios.spec jsonb is mig 145, not projects.scenarios as the RFC misstated

Phase 2 (inventor) gates on m's go. The inventor reads this + RFC + grills m on Tier 1 before sketching.
2026-05-27 11:03:33 +02:00