PRD: Procedures tool — columnar litigation planner (overview / event-triggered / case-file) #153

Open
opened 2026-05-27 20:19:58 +00:00 by mAi · 7 comments
Collaborator

Direction (m, 2026-05-27 22:18) — verbatim

I like to keep our current columnar layout with proactive / court / reactive. And it is good if we can select which side we want to simulate.

We now have all proceeding_types and the inner sequential connections worked out, I hope.

There are basically three main approaches I see to this: Get an overview over proceedings, play around with options, build Scenarios.

Another one where something specific happened and we just want to know what deadlines we need to note, what mandatory and optional follow-ups are connected to that event. We may want to add determined follow-ups for that event to a case file.

A third one from a specific proceeding / case file where things take place / have taken place. Some parameters are pre-defined here. And we want to determine the further course of proceedings.

We need to have a good UI/UX with solid tabs for approaches that do not belong together. We need to allow filtering by all types of dimensions for the proceeding type / court / party … and of course text search.

We need to keep it simple UI UX wise where we can select optional followups. They can be highlighted and "turned on" and we need some display options as to how many optional steps should show.

We will need a very good scoping of this with good questions. We put an inventor on the PRD.

The backend should be quite good for this exercise already. At it's core it is project planning with different relational events. So it is a litigation planning tool that we are building here, no less.

We focus on the UPC and build it with that, later we can expand.

Let's find a good way to include our sequences etc.

Locked constraints

  • Columnar layout with proactive | court | reactive columns. Selectable simulation side.
  • Three approaches as separate solid tabs:
    1. Overview / scenarios — explore a proceeding type, play with options, build scenarios.
    2. Event-triggered — "X happened — what now?" — surface mandatory + optional follow-ups; optionally pin determined follow-ups to a case file.
    3. Case-file driven — from a project/case with pre-defined parameters, determine further course.
  • Filtering across all dimensions (proceeding type, court, party, …) + text search.
  • Optional follow-ups are highlighted, toggle-able, with a display setting for how many optional steps to show.
  • Modular where possible.
  • UPC v1, expand later.

Backend snapshot (as of 2026-05-27)

  • paliad.proceeding_types — catalog with inner sequential connections (recently cleaned).
  • paliad.procedural_events — canonical events per proceeding (curie t-paliad-336 finished the null.* cleanup tonight).
  • paliad.sequencing_rules — anchor → event with timing / duration_value / duration_unit / trigger_event_id / primary_party / condition_expr / spawn semantics.
  • paliad.projects + scenario_flags SSoT (ritchie shipped P0 of #149).
  • Tonight's tracker-rev data fixes also landed: 9 party-direction corrections, 3 compound null.* stopgaps, R.109(1)+R.109(4) anchored to oral_hearing, R.109(1) renamed for disambiguation.
  • Known gaps: 7 compound-name rules on m's split-review backlog, 11 MULTI-ROOT proceeding_types needing chain consolidation, full P4 trigger_events deprecation (#149) still pending.

Predecessor designs to read before grilling

  • docs/design-procedures-workflow-tracker-2026-05-27.md (atlas, reverted — anchor+scope didn't land; m's blocking feedback at 22:08 said "the whole anchor and scope does not work well for me"). Read atlas's own debrief on m/paliad#152 — first-draft overengineered, grilling-round saved it but the eventual shape was still wrong.
  • docs/design-unified-procedural-events-tool-2026-05-27.md (cronus). The U0-U4 catalog/wizard/verfahrensablauf train that's currently live on main (ed3c5d1 post-revert) — visual baseline for the columnar shape m liked.
  • The reverted commits (2e6427d, 6506d7d, 702f786) are the cautionary tale: tracker shape, the design pattern (single timeline-tree with inline forks) is not what m wants. Do not re-propose.

Inventor task

Grill hard before writing — atlas's debrief on #152 said it best:

"When the architecture is novel, default to grilling m in prose FIRST (3-5 questions about the core metaphor) BEFORE writing a full doc with structured AskUserQuestion. The doc rewrites cost a commit; the bigger cost would have been wasting m's 11-question batch on the wrong architecture."

Sequence:

  1. Read atlas's tracker design + cronus's unified design + this issue thread + the live columnar shape (/tools/procedures proceeding tab on main, post-revert).
  2. Grill m in prose first — 3–5 core-metaphor questions: what "modular" actually means concretely (per-column? per-approach? per-event?); how the three approaches share or don't share state; how case-file integration works for approach 3; how optional-follow-up display setting interacts with filtering.
  3. Then structured AskUserQuestion batches (max 4 questions per batch, max ~12 total). Focus on hard decisions: filter axis set, scenario persistence, party-perspective default, optional-display granularity, etc.
  4. Draft docs/plans/prd-procedures-litigation-planner-2026-05-27.md (or similar slug). PRD shape: goals → user journeys for each of the three approaches → hard decisions table (m's picks + rationale) → data model deltas (if any) → modular boundaries → migration plan from current live shape → out-of-scope.
  5. Report DESIGN READY FOR REVIEW and park persistent. Head gates coder shift.

Out of scope: implementation slices, code, mig SQL drafting. PRD only.

Not atlas (just-rejected tracker → framing bias). Not cronus (parked on Fristenrechner). Fresh Opus inventor.

Why not just revive the old verfahrensablauf shape?

The old shape is the visual baseline but pre-dates the three-approach split and the modular requirement. We need an actual PRD here, not just a UI restore.

## Direction (m, 2026-05-27 22:18) — verbatim > I like to keep our current columnar layout with proactive / court / reactive. And it is good if we can select which side we want to simulate. > > We now have all proceeding_types and the inner sequential connections worked out, I hope. > > There are basically three main approaches I see to this: **Get an overview over proceedings, play around with options, build Scenarios.** > > Another one where **something specific happened** and we just want to know what deadlines we need to note, what mandatory and optional follow-ups are connected to that event. We may want to add determined follow-ups for that event to a case file. > > A third one **from a specific proceeding / case file where things take place / have taken place**. Some parameters are pre-defined here. And we want to determine the further course of proceedings. > > We need to have a good UI/UX with solid tabs for approaches that do not belong together. We need to allow filtering by all types of dimensions for the proceeding type / court / party … and of course text search. > > We need to keep it simple UI UX wise where we can select optional followups. They can be highlighted and "turned on" and we need some display options as to how many optional steps should show. > > We will need a very good scoping of this with good questions. We put an inventor on the PRD. > > The backend should be quite good for this exercise already. At it's core it is project planning with different relational events. So it is a litigation planning tool that we are building here, no less. > > We focus on the UPC and build it with that, later we can expand. > > Let's find a good way to include our sequences etc. ## Locked constraints - **Columnar layout** with `proactive | court | reactive` columns. Selectable simulation side. - **Three approaches as separate solid tabs**: 1. **Overview / scenarios** — explore a proceeding type, play with options, build scenarios. 2. **Event-triggered** — "X happened — what now?" — surface mandatory + optional follow-ups; optionally pin determined follow-ups to a case file. 3. **Case-file driven** — from a project/case with pre-defined parameters, determine further course. - **Filtering across all dimensions** (proceeding type, court, party, …) + text search. - **Optional follow-ups** are highlighted, toggle-able, with a display setting for how many optional steps to show. - **Modular where possible**. - **UPC v1**, expand later. ## Backend snapshot (as of 2026-05-27) - `paliad.proceeding_types` — catalog with inner sequential connections (recently cleaned). - `paliad.procedural_events` — canonical events per proceeding (curie t-paliad-336 finished the null.* cleanup tonight). - `paliad.sequencing_rules` — anchor → event with `timing` / `duration_value` / `duration_unit` / `trigger_event_id` / `primary_party` / `condition_expr` / spawn semantics. - `paliad.projects` + `scenario_flags` SSoT (ritchie shipped P0 of #149). - Tonight's tracker-rev data fixes also landed: 9 party-direction corrections, 3 compound `null.*` stopgaps, R.109(1)+R.109(4) anchored to oral_hearing, R.109(1) renamed for disambiguation. - Known gaps: 7 compound-name rules on m's split-review backlog, 11 MULTI-ROOT proceeding_types needing chain consolidation, full P4 trigger_events deprecation (#149) still pending. ## Predecessor designs to read before grilling - `docs/design-procedures-workflow-tracker-2026-05-27.md` (atlas, **reverted** — anchor+scope didn't land; m's blocking feedback at 22:08 said "the whole anchor and scope does not work well for me"). Read atlas's own debrief on m/paliad#152 — first-draft overengineered, grilling-round saved it but the eventual shape was still wrong. - `docs/design-unified-procedural-events-tool-2026-05-27.md` (cronus). The U0-U4 catalog/wizard/verfahrensablauf train that's currently live on main (`ed3c5d1` post-revert) — visual baseline for the columnar shape m liked. - The reverted commits (2e6427d, 6506d7d, 702f786) are the cautionary tale: tracker shape, the design pattern (single timeline-tree with inline forks) is **not** what m wants. Do not re-propose. ## Inventor task **Grill hard before writing — atlas's debrief on #152 said it best:** > "When the architecture is novel, default to grilling m in prose FIRST (3-5 questions about the core metaphor) BEFORE writing a full doc with structured AskUserQuestion. The doc rewrites cost a commit; the bigger cost would have been wasting m's 11-question batch on the wrong architecture." Sequence: 1. **Read** atlas's tracker design + cronus's unified design + this issue thread + the live columnar shape (`/tools/procedures` proceeding tab on main, post-revert). 2. **Grill m in prose first** — 3–5 core-metaphor questions: what "modular" actually means concretely (per-column? per-approach? per-event?); how the three approaches share or don't share state; how case-file integration works for approach 3; how optional-follow-up display setting interacts with filtering. 3. **Then structured AskUserQuestion batches** (max 4 questions per batch, max ~12 total). Focus on hard decisions: filter axis set, scenario persistence, party-perspective default, optional-display granularity, etc. 4. **Draft** `docs/plans/prd-procedures-litigation-planner-2026-05-27.md` (or similar slug). PRD shape: goals → user journeys for each of the three approaches → hard decisions table (m's picks + rationale) → data model deltas (if any) → modular boundaries → migration plan from current live shape → out-of-scope. 5. **Report `DESIGN READY FOR REVIEW`** and park persistent. Head gates coder shift. **Out of scope:** implementation slices, code, mig SQL drafting. PRD only. Not atlas (just-rejected tracker → framing bias). Not cronus (parked on Fristenrechner). Fresh Opus inventor. ## Why not just revive the old verfahrensablauf shape? The old shape is the visual baseline but pre-dates the three-approach split and the modular requirement. We need an actual PRD here, not just a UI restore.
mAi self-assigned this 2026-05-27 20:19:58 +00:00
Author
Collaborator

PRD landed → main at 2c2b93b. edison shipped docs/plans/prd-procedures-litigation-planner-2026-05-27.md (685 lines).

Shape (m's 20 picks captured across 5 grilling batches)

  • One canvas, three entry modes (Übersicht / Ereignis / Aus Akte) — entry modes shape the initial canvas state; once you're working, the canvas itself is the interface.
  • Multi-proceeding vertical column-triplets proaktiv | court | reaktiv, perspective-flippable per proceeding. CCR/amend child triplets auto-expand inline at spawn nodes.
  • Per-event-card optional horizon — the card is the unit of optional-display control ("+N Optionen ▾"), not a global toggle.
  • Scenario DB — 4 new tables (scenarios, scenario_proceedings, scenario_events, scenario_shares). Constellations live in the DB, not the URL. Auto-save by default.
  • Promote-to-project wizard (3 steps): confirm → fill parties → akte metadata. Scenario row links to project via promoted_project_id.
  • Read-only sharing with named HLC colleagues. Original owner stays sole editor.
  • Akte mode is project-backed — flag toggles and filed-state writes flow through to paliad.projects.scenario_flags and paliad.deadlines via existing endpoints.
  • Mobile basic-read in v1, full editing desktop-only.

Migration train (7 slices)

Slice Scope Visible?
B0 Scenario DB foundation: 4 new tables + RLS + minimal API. Dev-only test route at first. No user-visible change.
B1 Builder shell + cold-open mode. New /tools/procedures replaces 4-tab catalog. Single-triplet works end-to-end. New page visible.
B2 Multi-triplet stack + spawn nesting + per-event 3-state machine (planned/filed/skipped) + per-card optional horizon. Full scenario builder works without Akte.
B3 Event-triggered mode + universal search (events + scenarios + Akten). Event lookup works.
B4 Akte mode + project-backed scenarios. Dual-write: Akte-backed → paliad.deadlines + projects.scenario_flags; kontextfrei → scenario_events. Akte integration works end-to-end.
B5 Share + Promote-to-project wizard. Sharing buckets in side panel. Transactional promotion. Sharing + promotion work.
B6 Mobile basic-read + cleanup of dead U0-U4 catalog code + i18n polish. Mobile works; codebase cleaner.

Greenlit

m signed off 2026-05-27 23:03. Dispatching B0 only first (DB-only, low risk, reviewable in isolation). Pattern-fluent Sonnet coder. After B0 lands, m gates the B1+ shape (solo vs trained).

Parallel: party-direction sweep

youpcorg/head has been surfacing party-direction misclassifications all evening (RoP.029, RoP.052, RoP.056, RoP.118.4, now RoP.151-154 in upc.costs.cfi). m flagged the broader pattern — researcher (curie, recycled) running a systematic audit across all upc.* sequencing_rules where primary_party='court' but the event is actually party-filed.

**PRD landed → main at `2c2b93b`.** edison shipped `docs/plans/prd-procedures-litigation-planner-2026-05-27.md` (685 lines). ## Shape (m's 20 picks captured across 5 grilling batches) - **One canvas, three entry modes** (`Übersicht / Ereignis / Aus Akte`) — entry modes shape the initial canvas state; once you're working, the canvas itself is the interface. - **Multi-proceeding vertical column-triplets** `proaktiv | court | reaktiv`, perspective-flippable per proceeding. CCR/amend child triplets auto-expand inline at spawn nodes. - **Per-event-card optional horizon** — the card is the unit of optional-display control ("+N Optionen ▾"), not a global toggle. - **Scenario DB** — 4 new tables (`scenarios`, `scenario_proceedings`, `scenario_events`, `scenario_shares`). Constellations live in the DB, not the URL. Auto-save by default. - **Promote-to-project wizard** (3 steps): confirm → fill parties → akte metadata. Scenario row links to project via `promoted_project_id`. - **Read-only sharing** with named HLC colleagues. Original owner stays sole editor. - **Akte mode is project-backed** — flag toggles and filed-state writes flow through to `paliad.projects.scenario_flags` and `paliad.deadlines` via existing endpoints. - **Mobile basic-read** in v1, full editing desktop-only. ## Migration train (7 slices) | Slice | Scope | Visible? | |---|---|---| | **B0** | Scenario DB foundation: 4 new tables + RLS + minimal API. Dev-only test route at first. | No user-visible change. | | **B1** | Builder shell + cold-open mode. New `/tools/procedures` replaces 4-tab catalog. Single-triplet works end-to-end. | New page visible. | | **B2** | Multi-triplet stack + spawn nesting + per-event 3-state machine (planned/filed/skipped) + per-card optional horizon. | Full scenario builder works without Akte. | | **B3** | Event-triggered mode + universal search (events + scenarios + Akten). | Event lookup works. | | **B4** | Akte mode + project-backed scenarios. Dual-write: Akte-backed → `paliad.deadlines` + `projects.scenario_flags`; kontextfrei → `scenario_events`. | Akte integration works end-to-end. | | **B5** | Share + Promote-to-project wizard. Sharing buckets in side panel. Transactional promotion. | Sharing + promotion work. | | **B6** | Mobile basic-read + cleanup of dead U0-U4 catalog code + i18n polish. | Mobile works; codebase cleaner. | ## Greenlit m signed off 2026-05-27 23:03. Dispatching **B0 only** first (DB-only, low risk, reviewable in isolation). Pattern-fluent Sonnet coder. After B0 lands, m gates the B1+ shape (solo vs trained). ## Parallel: party-direction sweep youpcorg/head has been surfacing party-direction misclassifications all evening (RoP.029, RoP.052, RoP.056, RoP.118.4, now RoP.151-154 in upc.costs.cfi). m flagged the broader pattern — researcher (curie, recycled) running a systematic audit across all upc.* sequencing_rules where `primary_party='court'` but the event is actually party-filed.
Author
Collaborator

B0 landed → main at 1844df3. Schema foundation only, user-invisible per design.

What shipped (commit 0f3c30a)

  • Migration 157 (up + down SQL): extends paliad.scenarios with 6 builder columns + adds 3 sibling tables (scenario_proceedings, scenario_events, scenario_shares) + helper fn + RLS + paliad.projects.origin_scenario_id.
  • Service layer: internal/services/scenario_builder_service.go (936 lines) + _test.go (220 lines, pass clean).
  • Handlers: internal/handlers/scenario_builder.go (589 lines, minimal CRUD: list / create / get for scenarios + dev-only test endpoints).
  • Wired in cmd/server/main.go + handlers.go so the routes register on boot.
  • 8 files / 2374 insertions.

Post-merge: bun build clean, go vet ./... clean, go test ./internal/services/... pass.

Slice train status

Slice State
B0 Scenario DB foundation ✓ shipped (1844df3)
B1 Builder shell + cold-open pending — m gates next dispatch
B2 Multi-triplet + per-event 3-state pending
B3 Event-triggered mode + universal search pending
B4 Akte mode + project-backed scenarios pending
B5 Share + Promote-to-project wizard pending
B6 Mobile basic-read + cleanup pending

Next gate: m decides whether B1+ goes as one slice or as a trained shift.

**B0 landed → main at `1844df3`.** Schema foundation only, user-invisible per design. ## What shipped (commit `0f3c30a`) - **Migration 157** (up + down SQL): extends `paliad.scenarios` with 6 builder columns + adds 3 sibling tables (`scenario_proceedings`, `scenario_events`, `scenario_shares`) + helper fn + RLS + `paliad.projects.origin_scenario_id`. - **Service layer**: `internal/services/scenario_builder_service.go` (936 lines) + `_test.go` (220 lines, pass clean). - **Handlers**: `internal/handlers/scenario_builder.go` (589 lines, minimal CRUD: list / create / get for scenarios + dev-only test endpoints). - **Wired in `cmd/server/main.go`** + `handlers.go` so the routes register on boot. - **8 files / 2374 insertions**. Post-merge: `bun build` clean, `go vet ./...` clean, `go test ./internal/services/...` pass. ## Slice train status | Slice | State | |---|---| | **B0** Scenario DB foundation | ✓ shipped (`1844df3`) | | B1 Builder shell + cold-open | pending — m gates next dispatch | | B2 Multi-triplet + per-event 3-state | pending | | B3 Event-triggered mode + universal search | pending | | B4 Akte mode + project-backed scenarios | pending | | B5 Share + Promote-to-project wizard | pending | | B6 Mobile basic-read + cleanup | pending | Next gate: m decides whether B1+ goes as one slice or as a trained shift.
Author
Collaborator

Engine semantics fixed for t-paliad-342 — supports the Litigation Builder PRD path.

Commit: 3c840c0366

Two paired pkg/litigationplanner changes:

  1. trigger_event_id authoritative anchor (fixes RoP.109.5 fabricated-date bug). New CalcOptions.TriggerEventAnchors map[string]string keyed by paliad.trigger_events.code (e.g. oral_hearing). When a rule has trigger_event_id and no anchor is supplied, the rule renders as IsConditional (empty date) instead of falling back to the user's SoC date. New Timeline.RulesAwaitingAnchor int counter for caller telemetry.

  2. Optional-rule suppression by default. New CalcOptions.IncludeOptional bool (default false). Rules with priority='optional' no longer auto-fire alongside mandatory deadlines. Cascades through skippedIDs so child rules drop too. The 9 optional rules listed in youpcorg#2570 (RoP.323, .320, .019, .016, .262.2, .333, .118.4, .007.4, .109) no longer leak into the default timeline.

Frontend toggle ("show optional applications") is out of scope this slice — engine support only, per task brief.

Tests: 6 new focused tests (optional_and_trigger_anchor_test.go), 2 existing tests opt in to IncludeOptional: true to preserve their before-child-of-court-set-parent intent. go test ./pkg/litigationplanner/... + go test ./internal/services/... + go vet ./... + bun run build all clean.

Engine semantics fixed for t-paliad-342 — supports the Litigation Builder PRD path. Commit: https://mgit.msbls.de/m/paliad/commit/3c840c0366a6c3b5098bba4f6e71ff058aff51a7 Two paired pkg/litigationplanner changes: 1. **trigger_event_id authoritative anchor** (fixes RoP.109.5 fabricated-date bug). New `CalcOptions.TriggerEventAnchors map[string]string` keyed by `paliad.trigger_events.code` (e.g. `oral_hearing`). When a rule has trigger_event_id and no anchor is supplied, the rule renders as `IsConditional` (empty date) instead of falling back to the user's SoC date. New `Timeline.RulesAwaitingAnchor int` counter for caller telemetry. 2. **Optional-rule suppression by default**. New `CalcOptions.IncludeOptional bool` (default false). Rules with `priority='optional'` no longer auto-fire alongside mandatory deadlines. Cascades through `skippedIDs` so child rules drop too. The 9 optional rules listed in youpcorg#2570 (RoP.323, .320, .019, .016, .262.2, .333, .118.4, .007.4, .109) no longer leak into the default timeline. Frontend toggle ("show optional applications") is out of scope this slice — engine support only, per task brief. Tests: 6 new focused tests (`optional_and_trigger_anchor_test.go`), 2 existing tests opt in to `IncludeOptional: true` to preserve their before-child-of-court-set-parent intent. `go test ./pkg/litigationplanner/...` + `go test ./internal/services/...` + `go vet ./...` + `bun run build` all clean.
Author
Collaborator

B1 + B2 shipped (galileo, shift-1) — branch mai/galileo/coder-b1-b2-mvp-train.

The Litigation Builder MVP train is on the branch. After B2 a user can build and save a multi-proceeding scenario kontextfrei (PRD §7.2 — that alone replaces 60% of today's catalog use).

Commits:

  • B1 — 6c1d8cc feat(builder): B1 — Litigation Builder shell + cold-open mode
  • B2 — 46dc4ec feat(builder): B2 — multi-triplet stack + spawn nesting + per-event state

B1 (PRD §7.1):

  • New /tools/procedures page replaces cronus's 4-tab catalog: page header (scenario picker + Akte stub + Stichtag + search), entry-mode radio (cold-open active; event/akte disabled with B3/B4 tooltips), side panel with Aktiv bucket.
  • Cold-open canvas with empty state + "Neues Szenario starten" CTA + 5-most-recent list.
  • Add-proceeding inline picker (Forum chip row + Verfahren chip row, UPC v1).
  • First triplet renders end-to-end via verfahrensablauf-core.calculateDeadlines + renderColumnsBody (read-only in B1).
  • Auto-save with 500ms debounce on name + stichtag patches. Save status indicator (idle → saving → saved / error) in the page header.
  • New modules: frontend/src/client/builder.ts (orchestrator), builder-picker.ts (inline popover), builder-triplet.ts (header + body wrapper). procedures.tsx rewritten as shell, client/procedures.ts now just boots the builder.

B2 (PRD §7.1):

  • Multi-triplet stack with + Verfahren hinzufügen at the bottom; top-level proceedings sorted by ordinal, child triplets nested inline with a left lime border.
  • Per-triplet controls bar: perspective radio (none / claimant / defendant), Detailgrad pill, Entfernen.
  • Per-triplet flag strip: every paliad.scenario_flag_catalog row as a checkbox, bound to scenario_proceedings.scenario_flags. Active flags also surface as header chips.
  • Spawn nesting: with_ccr ON on upc.inf.cfi auto-POSTs a upc.ccr.cfi child with parent_scenario_proceeding_id; OFF deletes the child (events cascade via schema). SPAWN_MAP is data-driven so future flags slot in.
  • 3-state event cards (planned / filed / skipped): overlayEventStates walks .fr-col-item[data-rule-id] nodes (the new hook added to verfahrensablauf-core in this slice) and stamps data-builder-state + per-state action buttons. POSTs or PATCHes paliad.scenario_events keyed by sequencing_rule_id.
  • Per-card optional-horizon chip persisted via scenario_events.horizon_optional (increment / decrement); full surface awaits a calc-engine "optionals available" counter (PRD §3.4 follow-up — persistence + UX hook in place so the wiring lands without another schema touch).
  • Backend: one new read-only endpoint GET /api/builder/scenario-flag-catalog passes through ScenarioFlagsService.ListCatalog so the builder doesn't need a per-project round-trip to render flag toggles.

Verified: bun run build clean (3050 i18n keys, 0 stragglers), go vet ./... clean, go test ./... clean (all packages), bun test src/client/views/verfahrensablauf-core.test.ts 57/57 pass — the data-rule-id stamp is non-breaking. Server boots cleanly with the new routes.

Not in scope (head gates these): B3 event-triggered + universal search, B4 Akte mode + deadline dual-write, B5 share + promote-to-project wizard, B6 mobile + dead-code cleanup. PRD §7.4 cleanup of the legacy U0-U4 files (verfahrensablauf.ts, VerfahrensablaufBody.tsx, etc.) is deferred to B6 per the train design — they're unreachable from /tools/procedures now but still on disk.

**B1 + B2 shipped (galileo, shift-1)** — branch `mai/galileo/coder-b1-b2-mvp-train`. The Litigation Builder MVP train is on the branch. After B2 a user can build and save a multi-proceeding scenario kontextfrei (PRD §7.2 — that alone replaces 60% of today's catalog use). **Commits:** - B1 — `6c1d8cc` feat(builder): B1 — Litigation Builder shell + cold-open mode - B2 — `46dc4ec` feat(builder): B2 — multi-triplet stack + spawn nesting + per-event state **B1 (PRD §7.1):** - New `/tools/procedures` page replaces cronus's 4-tab catalog: page header (scenario picker + Akte stub + Stichtag + search), entry-mode radio (cold-open active; event/akte disabled with B3/B4 tooltips), side panel with Aktiv bucket. - Cold-open canvas with empty state + "Neues Szenario starten" CTA + 5-most-recent list. - Add-proceeding inline picker (Forum chip row + Verfahren chip row, UPC v1). - First triplet renders end-to-end via `verfahrensablauf-core.calculateDeadlines` + `renderColumnsBody` (read-only in B1). - Auto-save with 500ms debounce on name + stichtag patches. Save status indicator (idle → saving → saved / error) in the page header. - New modules: `frontend/src/client/builder.ts` (orchestrator), `builder-picker.ts` (inline popover), `builder-triplet.ts` (header + body wrapper). `procedures.tsx` rewritten as shell, `client/procedures.ts` now just boots the builder. **B2 (PRD §7.1):** - Multi-triplet stack with `+ Verfahren hinzufügen` at the bottom; top-level proceedings sorted by ordinal, child triplets nested inline with a left lime border. - Per-triplet controls bar: perspective radio (none / claimant / defendant), Detailgrad pill, Entfernen. - Per-triplet flag strip: every `paliad.scenario_flag_catalog` row as a checkbox, bound to `scenario_proceedings.scenario_flags`. Active flags also surface as header chips. - Spawn nesting: `with_ccr` ON on `upc.inf.cfi` auto-POSTs a `upc.ccr.cfi` child with `parent_scenario_proceeding_id`; OFF deletes the child (events cascade via schema). `SPAWN_MAP` is data-driven so future flags slot in. - 3-state event cards (planned / filed / skipped): `overlayEventStates` walks `.fr-col-item[data-rule-id]` nodes (the new hook added to `verfahrensablauf-core` in this slice) and stamps `data-builder-state` + per-state action buttons. POSTs or PATCHes `paliad.scenario_events` keyed by `sequencing_rule_id`. - Per-card optional-horizon chip persisted via `scenario_events.horizon_optional` (increment / decrement); full surface awaits a calc-engine "optionals available" counter (PRD §3.4 follow-up — persistence + UX hook in place so the wiring lands without another schema touch). - Backend: one new read-only endpoint `GET /api/builder/scenario-flag-catalog` passes through `ScenarioFlagsService.ListCatalog` so the builder doesn't need a per-project round-trip to render flag toggles. **Verified:** `bun run build` clean (3050 i18n keys, 0 stragglers), `go vet ./...` clean, `go test ./...` clean (all packages), `bun test src/client/views/verfahrensablauf-core.test.ts` 57/57 pass — the data-rule-id stamp is non-breaking. Server boots cleanly with the new routes. **Not in scope (head gates these):** B3 event-triggered + universal search, B4 Akte mode + deadline dual-write, B5 share + promote-to-project wizard, B6 mobile + dead-code cleanup. PRD §7.4 cleanup of the legacy U0-U4 files (`verfahrensablauf.ts`, `VerfahrensablaufBody.tsx`, etc.) is deferred to B6 per the train design — they're unreachable from `/tools/procedures` now but still on disk.
Author
Collaborator

Fix landed: t-paliad-344 — prod 500 storm after B0+B1+B2 deploy

Root cause (one bug, one unrelated bonus):

1. /api/events?type=deadline 500 on every dashboard hit

Mig 140 (Slice B.4, commit 1129bab, 2026-05-26 19:53) dropped paliad.deadlines.rule_id and renamed the back-link to sequencing_rule_id. The accompanying code change updated the JOIN to f.sequencing_rule_id but left the SELECT projection on f.rule_id in two places:

  • internal/services/deadline_service.go:268DeadlineService.ListVisibleForUser (powers /api/events?type=deadline, the entire dashboard deadline rail, /deadlines page, every status bucket).
  • internal/services/projection_service.go:1250collectActualsForOverrides (projection passes, less noisy).

Live container logs since the post-B0 container came up at 22:31:59 (DB had mig 140, code had stale SELECT): a burst of 60+ pq: column f.rule_id does not exist at position 3:36 (42703) lines — one per dashboard poll. The model tag is db:"sequencing_rule_id" json:"rule_id", so the wire shape is unchanged — only the column reference flips.

2. /tools/procedures null.filter(...) crash

ScenarioBuilderService.GetScenarioDeep initialised Proceedings / Events / Shares as zero-value (nil) slices. Go's encoding/json serialises nil slices as null. When the builder loaded a zero-row scenario, state.active.proceedings was null, and renderCanvas unconditionally called .filter(...) on it → procedures.js:101 TypeError: Cannot read properties of null (reading 'filter').

(The original report's surmise about /api/builder/scenarios?status=active returning 500 turned out to be a misread — there were zero builder-side errors in container logs. The actual 500 was /api/events, and the JS crash was the unrelated null-slice bug.)

Fix

Two commits on mai/brunel/fixer-prod-500s-after-b1 → merged via #154 (1f6e586):

  • a905911 — SELECT columns: f.rule_idf.sequencing_rule_id in deadline_service.go; d.rule_idd.sequencing_rule_id AS rule_id in projection_service.go (alias preserves the receiving struct tag).
  • a4b865dBuilderScenarioDeep initialises sub-arrays to [] so the JSON wire is always arrays; builder.ts loadScenario normalises arrays defensively as well.

bun build clean, go vet ./... clean, go test ./... green, EXPLAIN against the live schema clean.

Verification post-merge

Container redeployed at 22:50:23 (compose-transmit-multi-byte-driver-v7jth9-web-1 on mlake). Boot log shows clean migration apply + server start; no more rule_id does not exist lines in the post-redeploy window. Monitor armed on the container for another 25 min to catch any regression.

Why CI didn't catch this

The deadline integration tests (internal/services/deadline_service_test.go, event_service_test.go) DO call ListVisibleForUser against a real DB — but they're gated on TEST_DATABASE_URL and skipped without it. CI doesn't set the var, so the post-mig-140 schema was never exercised by the suite. Suggested follow-up: a separate task to wire TEST_DATABASE_URL into CI against a scratch DB.

## Fix landed: t-paliad-344 — prod 500 storm after B0+B1+B2 deploy **Root cause** (one bug, one unrelated bonus): ### 1. `/api/events?type=deadline` 500 on every dashboard hit Mig 140 (Slice B.4, commit `1129bab`, 2026-05-26 19:53) **dropped** `paliad.deadlines.rule_id` and renamed the back-link to `sequencing_rule_id`. The accompanying code change updated the JOIN to `f.sequencing_rule_id` but **left the SELECT projection on `f.rule_id`** in two places: - `internal/services/deadline_service.go:268` — `DeadlineService.ListVisibleForUser` (powers `/api/events?type=deadline`, the entire dashboard deadline rail, `/deadlines` page, every status bucket). - `internal/services/projection_service.go:1250` — `collectActualsForOverrides` (projection passes, less noisy). Live container logs since the post-B0 container came up at 22:31:59 (DB had mig 140, code had stale SELECT): **a burst of 60+ `pq: column f.rule_id does not exist at position 3:36 (42703)` lines** — one per dashboard poll. The model tag is `db:"sequencing_rule_id" json:"rule_id"`, so the wire shape is unchanged — only the column reference flips. ### 2. `/tools/procedures` `null.filter(...)` crash `ScenarioBuilderService.GetScenarioDeep` initialised `Proceedings` / `Events` / `Shares` as zero-value (nil) slices. Go's `encoding/json` serialises nil slices as `null`. When the builder loaded a zero-row scenario, `state.active.proceedings` was `null`, and `renderCanvas` unconditionally called `.filter(...)` on it → `procedures.js:101 TypeError: Cannot read properties of null (reading 'filter')`. (The original report's surmise about `/api/builder/scenarios?status=active` returning 500 turned out to be a misread — there were zero builder-side errors in container logs. The actual 500 was `/api/events`, and the JS crash was the unrelated null-slice bug.) ## Fix Two commits on `mai/brunel/fixer-prod-500s-after-b1` → merged via #154 (`1f6e586`): - `a905911` — SELECT columns: `f.rule_id` → `f.sequencing_rule_id` in `deadline_service.go`; `d.rule_id` → `d.sequencing_rule_id AS rule_id` in `projection_service.go` (alias preserves the receiving struct tag). - `a4b865d` — `BuilderScenarioDeep` initialises sub-arrays to `[]` so the JSON wire is always arrays; `builder.ts` `loadScenario` normalises arrays defensively as well. `bun build` clean, `go vet ./...` clean, `go test ./...` green, `EXPLAIN` against the live schema clean. ## Verification post-merge Container redeployed at `22:50:23` (`compose-transmit-multi-byte-driver-v7jth9-web-1` on mlake). Boot log shows clean migration apply + server start; **no more `rule_id does not exist` lines** in the post-redeploy window. Monitor armed on the container for another 25 min to catch any regression. ## Why CI didn't catch this The deadline integration tests (`internal/services/deadline_service_test.go`, `event_service_test.go`) DO call `ListVisibleForUser` against a real DB — but they're gated on `TEST_DATABASE_URL` and skipped without it. CI doesn't set the var, so the post-mig-140 schema was never exercised by the suite. **Suggested follow-up**: a separate task to wire `TEST_DATABASE_URL` into CI against a scratch DB.
Author
Collaborator

t-paliad-348 — port engine semantics into Builder triplet + manuscript regen

Diagnosis (m flagged 2026-05-28 10:34): upc.inf.cfi triplet on /tools/procedures shows 29 cards on prod — 7 mandatory backbone events + 22 conditional rows (Lodging of translations, Mängelbeseitigung, Antrag auf Verweisung, Wiedereinsetzung, …). Live API verified:

// POST /api/tools/fristenrechner upc.inf.cfi
// → 19 deadlines, rulesAwaitingAnchor=11
// after filterByDetailMode('selected') → 7

Root cause: pasteur's engine fix (3c840c0IncludeOptional=false default + trigger_event_id semantic anchoring) already in main suppresses optionals server-side and leaves mandatory rules with unsatisfied trigger_event_id as IsConditional rows. The Litigation Builder triplet rendered the full response without applying the filterByDetailMode('selected') pass the legacy /tools/verfahrensablauf has used since the detail-mode P3 ship.

Before (live paliad.de):

BEFORE — 29 cards on upc.inf.cfi triplet, 22 conditional

After (DOM-injected filter simulating the post-fix render):

AFTER — 7 mandatory backbone cards

Fix layers (one commit per concern):

  1. a815818 — TS calc + Go handler + Builder triplet wiring.

    • internal/handlers/fristenrechner.go accepts includeOptional + triggerEventAnchors, forwards to services.CalcOptions.
    • frontend/src/client/views/verfahrensablauf-core.ts plumbs both into CalcParams → fetch body; adds rulesAwaitingAnchor to DeadlineResponse.
    • frontend/src/client/builder.ts hydrateTriplet applies filterByDetailMode(detailgrad) before renderColumnsBody; passes includeOptional=true when detailgrad='all_options'.
    • frontend/src/client/verfahrensablauf.ts aligns the legacy callsite + adds a hasOptionalOptIn helper so per-rule rule:<uuid>=true deviations still surface in 'selected' mode.
    • Pinned by frontend/src/client/views/verfahrensablauf-core.test.tsincludeOptional=true and triggerEventAnchors={…} round-trip through the request body; empty/default values are omitted so the wire stays minimal.
  2. 05247d7 — manuscript regen.

    • exports/gen-deadline-list.py canonicalises the one-off /tmp/paliad-deadline-export.py (work/head delegation #2572) with a SQL-level priority != 'optional' filter; --include-optional opts back in for an exhaustive dump.
    • Regenerated exports/upc-deadlines-2026-05-28.md: 178 rules across 25 proceedings (down from the unfiltered run). upc.inf.cfi drops from ~37 to 28 rows — the 9 optional rules removed; trigger_event_id-anchored mandatory rules stay (PA-knowable surface, runtime anchor state is a separate concern).

Build/tests: bun build + bun test (269 pass) + go vet ./... + go test ./internal/handlers/... ./pkg/litigationplanner/... all clean.

Branch: mai/archimedes/fixer-port-engine — ready for head merge.

Parallel safety: touched builder.ts only in hydrateTriplet (the calc surface, not the mode-state orchestration maxwell's B4 shift owns). No spillover into scenario_builder_service.go or new handlers.

### t-paliad-348 — port engine semantics into Builder triplet + manuscript regen Diagnosis (m flagged 2026-05-28 10:34): upc.inf.cfi triplet on /tools/procedures shows 29 cards on prod — 7 mandatory backbone events + **22 conditional rows** (Lodging of translations, Mängelbeseitigung, Antrag auf Verweisung, Wiedereinsetzung, …). Live API verified: ```js // POST /api/tools/fristenrechner upc.inf.cfi // → 19 deadlines, rulesAwaitingAnchor=11 // after filterByDetailMode('selected') → 7 ``` Root cause: pasteur's engine fix (3c840c0 — `IncludeOptional=false` default + `trigger_event_id` semantic anchoring) already in main suppresses optionals server-side **and** leaves mandatory rules with unsatisfied trigger_event_id as `IsConditional` rows. The Litigation Builder triplet rendered the full response without applying the `filterByDetailMode('selected')` pass the legacy /tools/verfahrensablauf has used since the detail-mode P3 ship. Before (live paliad.de): ![BEFORE — 29 cards on upc.inf.cfi triplet, 22 conditional](https://mgit.msbls.de/m/paliad/raw/branch/mai/archimedes/fixer-port-engine/exports/screenshots/paliad-348-before-upc-inf-cfi.png) After (DOM-injected filter simulating the post-fix render): ![AFTER — 7 mandatory backbone cards](https://mgit.msbls.de/m/paliad/raw/branch/mai/archimedes/fixer-port-engine/exports/screenshots/paliad-348-after-upc-inf-cfi.png) **Fix layers (one commit per concern):** 1. [a815818](https://mgit.msbls.de/m/paliad/commit/a815818) — TS calc + Go handler + Builder triplet wiring. - `internal/handlers/fristenrechner.go` accepts `includeOptional` + `triggerEventAnchors`, forwards to `services.CalcOptions`. - `frontend/src/client/views/verfahrensablauf-core.ts` plumbs both into `CalcParams` → fetch body; adds `rulesAwaitingAnchor` to `DeadlineResponse`. - `frontend/src/client/builder.ts` `hydrateTriplet` applies `filterByDetailMode(detailgrad)` before `renderColumnsBody`; passes `includeOptional=true` when `detailgrad='all_options'`. - `frontend/src/client/verfahrensablauf.ts` aligns the legacy callsite + adds a `hasOptionalOptIn` helper so per-rule `rule:<uuid>=true` deviations still surface in 'selected' mode. - Pinned by `frontend/src/client/views/verfahrensablauf-core.test.ts` — `includeOptional=true` and `triggerEventAnchors={…}` round-trip through the request body; empty/default values are omitted so the wire stays minimal. 2. [05247d7](https://mgit.msbls.de/m/paliad/commit/05247d7) — manuscript regen. - `exports/gen-deadline-list.py` canonicalises the one-off `/tmp/paliad-deadline-export.py` (work/head delegation #2572) with a SQL-level `priority != 'optional'` filter; `--include-optional` opts back in for an exhaustive dump. - Regenerated `exports/upc-deadlines-2026-05-28.md`: 178 rules across 25 proceedings (down from the unfiltered run). upc.inf.cfi drops from ~37 to 28 rows — the 9 optional rules removed; trigger_event_id-anchored mandatory rules stay (PA-knowable surface, runtime anchor state is a separate concern). **Build/tests:** `bun build` + `bun test` (269 pass) + `go vet ./...` + `go test ./internal/handlers/... ./pkg/litigationplanner/...` all clean. **Branch:** `mai/archimedes/fixer-port-engine` — ready for head merge. _Parallel safety:_ touched `builder.ts` only in `hydrateTriplet` (the calc surface, not the mode-state orchestration maxwell's B4 shift owns). No spillover into `scenario_builder_service.go` or new handlers.
Author
Collaborator

B5 + B6 — Litigation Builder train complete (t-paliad-350, coder=planck)

Branch mai/planck/coder-b5-b6-train-share pushed; awaiting head merge to main. This finishes the m/paliad#153 slice train — the Builder is now feature-complete through B6.

Commits

  • B5 — Share + Promote-to-project wizard: d913f4fc30
  • B6 — Mobile basic-read + dead U0-U4 cleanup + i18n finalise: 264cc39a6b

B5 — what shipped

  • Transactional promote (PRD §10, no partial promotions): ScenarioBuilderService.PromoteScenario runs one Postgres tx — INSERT paliad.projects ('case', origin_scenario_id, proceeding_type_id + scenario_flags from the primary triplet) → creator team lead + wizard-picked colleagues → parties → deadlines (filed→completed, planned→pending with computed/actual date, skipped→none) → scenario status='promoted' + promoted_project_id. Primary top-level proceeding + its spawn-descendants form the one case file; extra standalone proceedings stay in the scenario (reported via ProceedingsSkipped). Planned dates computed via the injected FristenrechnerService.
  • ListSharedWithMe + GET /api/builder/scenarios/shared, POST /api/builder/scenarios/{id}/promote.
  • Frontend: share modal (HLC user picker + revoke), 3-step promote wizard (Bestätigen → Parteien ergänzen → Akte-Metadaten), bucketed side panel (Aktiv / Geteilt mit mir / Als Projekt angelegt / Archiviert), read-only watermark + locked affordances for shared/promoted scenarios, deep-link auto-load now covers shared scenarios.

B6 — what shipped

  • Mobile (<640px): column-triplets collapse to one column (reused .fr-columns-view); a capture-phase guard intercepts mutating taps and shows the "Auf größerem Bildschirm öffnen" toast; reading stays functional. Desktop code paths unchanged.
  • Deleted dead U0-U4 catalog code (10 files: fristenrechner-mode-a/result/wizard, verfahrensablauf.ts, event-card-choices, verfahrensablauf-state, VerfahrensablaufBody.tsx, +3 tests). grep "from.*fristenrechner-|from.*verfahrensablauf" shows only the kept verfahrensablauf-core/-detail-mode the builder reuses. /tools/fristenrechner + /tools/verfahrensablauf remain 301 redirects.
  • i18n finalised DE+EN: removed 4 duplicate deadlines.party.* keys/block (behaviour-preserving) + added missing DE cal.today; no dupes, full parity.

Verification (Playwright, local server vs. TEST_DATABASE_URL)

  • Backend: live-DB TestScenarioBuilderPromote pins the transactional cascade (project + 2 deadlines + party created, scenario→promoted with back-ref, read-only-after-promote, re-promote rejected). go build/vet/test clean; bun build + 227 frontend tests pass.
  • Journey E (share): owner opens "Teilen" → user picker (live /api/users) → shared with a 2nd user → share row written. 2nd user: scenario appears in "Geteilt mit mir", and both the bucket-click and a direct deep-link auto-load it read-only with watermark "Geteilt von Planck Owner · schreibgeschützt" + canvas controls pointer-events:none. (A deep-link auto-load gap for shared scenarios was caught here and fixed.)
  • Journey D (promote): owner walks the 3-step wizard (party "Becker GmbH" added in step 2, title in step 3) → commit → navigates to /projects/{id}; cross-check: project has the party, scenario flipped to promoted with promoted_project_id back-ref, promoted bucket populated.
  • B6 mobile @375px: triplet collapses to 1 column + scenario list reads; "+ Verfahren hinzufügen" and "Teilen" are blocked (toast shown, no action, no proceeding added). @1280px the same actions work normally (guard early-returns off-mobile).
  • Authoring page (/admin/templates) + docforge generation untouched — no changes to that code; shared i18n + deleted dead files verified via clean build + passing tests.
## B5 + B6 — Litigation Builder train complete (t-paliad-350, coder=planck) Branch `mai/planck/coder-b5-b6-train-share` pushed; awaiting head merge to main. This finishes the m/paliad#153 slice train — the Builder is now feature-complete through B6. ### Commits - **B5 — Share + Promote-to-project wizard**: [`d913f4fc30`](https://mgit.msbls.de/m/paliad/commit/d913f4fc30e860c41cd8a3c330daff7dd57d86cc) - **B6 — Mobile basic-read + dead U0-U4 cleanup + i18n finalise**: [`264cc39a6b`](https://mgit.msbls.de/m/paliad/commit/264cc39a6bbaf8480531ca4c5e37009069379423) ### B5 — what shipped - **Transactional promote** (PRD §10, no partial promotions): `ScenarioBuilderService.PromoteScenario` runs one Postgres tx — INSERT `paliad.projects` ('case', `origin_scenario_id`, proceeding_type_id + scenario_flags from the primary triplet) → creator team lead + wizard-picked colleagues → parties → deadlines (filed→completed, planned→pending with computed/actual date, skipped→none) → scenario `status='promoted'` + `promoted_project_id`. Primary top-level proceeding + its spawn-descendants form the one case file; extra standalone proceedings stay in the scenario (reported via `ProceedingsSkipped`). Planned dates computed via the injected `FristenrechnerService`. - `ListSharedWithMe` + `GET /api/builder/scenarios/shared`, `POST /api/builder/scenarios/{id}/promote`. - Frontend: share modal (HLC user picker + revoke), 3-step promote wizard (Bestätigen → Parteien ergänzen → Akte-Metadaten), bucketed side panel (Aktiv / Geteilt mit mir / Als Projekt angelegt / Archiviert), read-only watermark + locked affordances for shared/promoted scenarios, deep-link auto-load now covers shared scenarios. ### B6 — what shipped - Mobile (<640px): column-triplets collapse to one column (reused `.fr-columns-view`); a capture-phase guard intercepts mutating taps and shows the "Auf größerem Bildschirm öffnen" toast; reading stays functional. Desktop code paths unchanged. - Deleted dead U0-U4 catalog code (10 files: fristenrechner-mode-a/result/wizard, verfahrensablauf.ts, event-card-choices, verfahrensablauf-state, VerfahrensablaufBody.tsx, +3 tests). `grep "from.*fristenrechner-|from.*verfahrensablauf"` shows only the kept `verfahrensablauf-core`/`-detail-mode` the builder reuses. /tools/fristenrechner + /tools/verfahrensablauf remain 301 redirects. - i18n finalised DE+EN: removed 4 duplicate `deadlines.party.*` keys/block (behaviour-preserving) + added missing DE `cal.today`; no dupes, full parity. ### Verification (Playwright, local server vs. TEST_DATABASE_URL) - **Backend**: live-DB `TestScenarioBuilderPromote` pins the transactional cascade (project + 2 deadlines + party created, scenario→promoted with back-ref, read-only-after-promote, re-promote rejected). `go build/vet/test` clean; `bun build` + 227 frontend tests pass. - **Journey E (share)**: owner opens "Teilen" → user picker (live /api/users) → shared with a 2nd user → share row written. 2nd user: scenario appears in "Geteilt mit mir", and both the bucket-click **and a direct deep-link** auto-load it read-only with watermark "Geteilt von Planck Owner · schreibgeschützt" + canvas controls `pointer-events:none`. (A deep-link auto-load gap for shared scenarios was caught here and fixed.) - **Journey D (promote)**: owner walks the 3-step wizard (party "Becker GmbH" added in step 2, title in step 3) → commit → navigates to /projects/{id}; cross-check: project has the party, scenario flipped to promoted with promoted_project_id back-ref, promoted bucket populated. - **B6 mobile** @375px: triplet collapses to 1 column + scenario list reads; "+ Verfahren hinzufügen" and "Teilen" are blocked (toast shown, no action, no proceeding added). @1280px the same actions work normally (guard early-returns off-mobile). - Authoring page (/admin/templates) + docforge generation untouched — no changes to that code; shared i18n + deleted dead files verified via clean build + passing tests.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/paliad#153
No description provided.