PRD: Procedures tool — columnar litigation planner (overview / event-triggered / case-file) #153
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Direction (m, 2026-05-27 22:18) — verbatim
Locked constraints
proactive | court | reactivecolumns. Selectable simulation side.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 withtiming/duration_value/duration_unit/trigger_event_id/primary_party/condition_expr/ spawn semantics.paliad.projects+scenario_flagsSSoT (ritchie shipped P0 of #149).null.*stopgaps, R.109(1)+R.109(4) anchored to oral_hearing, R.109(1) renamed for disambiguation.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 (ed3c5d1post-revert) — visual baseline for the columnar shape m liked.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:
Sequence:
/tools/proceduresproceeding tab on main, post-revert).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.DESIGN READY FOR REVIEWand 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.
PRD landed → main at
2c2b93b. edison shippeddocs/plans/prd-procedures-litigation-planner-2026-05-27.md(685 lines).Shape (m's 20 picks captured across 5 grilling batches)
Übersicht / Ereignis / Aus Akte) — entry modes shape the initial canvas state; once you're working, the canvas itself is the interface.proaktiv | court | reaktiv, perspective-flippable per proceeding. CCR/amend child triplets auto-expand inline at spawn nodes.scenarios,scenario_proceedings,scenario_events,scenario_shares). Constellations live in the DB, not the URL. Auto-save by default.promoted_project_id.paliad.projects.scenario_flagsandpaliad.deadlinesvia existing endpoints.Migration train (7 slices)
/tools/proceduresreplaces 4-tab catalog. Single-triplet works end-to-end.paliad.deadlines+projects.scenario_flags; kontextfrei →scenario_events.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.B0 landed → main at
1844df3. Schema foundation only, user-invisible per design.What shipped (commit
0f3c30a)paliad.scenarioswith 6 builder columns + adds 3 sibling tables (scenario_proceedings,scenario_events,scenario_shares) + helper fn + RLS +paliad.projects.origin_scenario_id.internal/services/scenario_builder_service.go(936 lines) +_test.go(220 lines, pass clean).internal/handlers/scenario_builder.go(589 lines, minimal CRUD: list / create / get for scenarios + dev-only test endpoints).cmd/server/main.go+handlers.goso the routes register on boot.Post-merge:
bun buildclean,go vet ./...clean,go test ./internal/services/...pass.Slice train status
1844df3)Next gate: m decides whether B1+ goes as one slice or as a trained shift.
Engine semantics fixed for t-paliad-342 — supports the Litigation Builder PRD path.
Commit:
3c840c0366Two paired pkg/litigationplanner changes:
trigger_event_id authoritative anchor (fixes RoP.109.5 fabricated-date bug). New
CalcOptions.TriggerEventAnchors map[string]stringkeyed bypaliad.trigger_events.code(e.g.oral_hearing). When a rule has trigger_event_id and no anchor is supplied, the rule renders asIsConditional(empty date) instead of falling back to the user's SoC date. NewTimeline.RulesAwaitingAnchor intcounter for caller telemetry.Optional-rule suppression by default. New
CalcOptions.IncludeOptional bool(default false). Rules withpriority='optional'no longer auto-fire alongside mandatory deadlines. Cascades throughskippedIDsso 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 toIncludeOptional: trueto preserve their before-child-of-court-set-parent intent.go test ./pkg/litigationplanner/...+go test ./internal/services/...+go vet ./...+bun run buildall clean.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:
6c1d8ccfeat(builder): B1 — Litigation Builder shell + cold-open mode46dc4ecfeat(builder): B2 — multi-triplet stack + spawn nesting + per-event stateB1 (PRD §7.1):
/tools/procedurespage 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.verfahrensablauf-core.calculateDeadlines+renderColumnsBody(read-only in B1).frontend/src/client/builder.ts(orchestrator),builder-picker.ts(inline popover),builder-triplet.ts(header + body wrapper).procedures.tsxrewritten as shell,client/procedures.tsnow just boots the builder.B2 (PRD §7.1):
+ Verfahren hinzufügenat the bottom; top-level proceedings sorted by ordinal, child triplets nested inline with a left lime border.paliad.scenario_flag_catalogrow as a checkbox, bound toscenario_proceedings.scenario_flags. Active flags also surface as header chips.with_ccrON onupc.inf.cfiauto-POSTs aupc.ccr.cfichild withparent_scenario_proceeding_id; OFF deletes the child (events cascade via schema).SPAWN_MAPis data-driven so future flags slot in.overlayEventStateswalks.fr-col-item[data-rule-id]nodes (the new hook added toverfahrensablauf-corein this slice) and stampsdata-builder-state+ per-state action buttons. POSTs or PATCHespaliad.scenario_eventskeyed bysequencing_rule_id.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).GET /api/builder/scenario-flag-catalogpasses throughScenarioFlagsService.ListCatalogso the builder doesn't need a per-project round-trip to render flag toggles.Verified:
bun run buildclean (3050 i18n keys, 0 stragglers),go vet ./...clean,go test ./...clean (all packages),bun test src/client/views/verfahrensablauf-core.test.ts57/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/proceduresnow but still on disk.Fix landed: t-paliad-344 — prod 500 storm after B0+B1+B2 deploy
Root cause (one bug, one unrelated bonus):
1.
/api/events?type=deadline500 on every dashboard hitMig 140 (Slice B.4, commit
1129bab, 2026-05-26 19:53) droppedpaliad.deadlines.rule_idand renamed the back-link tosequencing_rule_id. The accompanying code change updated the JOIN tof.sequencing_rule_idbut left the SELECT projection onf.rule_idin two places:internal/services/deadline_service.go:268—DeadlineService.ListVisibleForUser(powers/api/events?type=deadline, the entire dashboard deadline rail,/deadlinespage, 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 isdb:"sequencing_rule_id" json:"rule_id", so the wire shape is unchanged — only the column reference flips.2.
/tools/proceduresnull.filter(...)crashScenarioBuilderService.GetScenarioDeepinitialisedProceedings/Events/Sharesas zero-value (nil) slices. Go'sencoding/jsonserialises nil slices asnull. When the builder loaded a zero-row scenario,state.active.proceedingswasnull, andrenderCanvasunconditionally 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=activereturning 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_idindeadline_service.go;d.rule_id→d.sequencing_rule_id AS rule_idinprojection_service.go(alias preserves the receiving struct tag).a4b865d—BuilderScenarioDeepinitialises sub-arrays to[]so the JSON wire is always arrays;builder.tsloadScenarionormalises arrays defensively as well.bun buildclean,go vet ./...clean,go test ./...green,EXPLAINagainst the live schema clean.Verification post-merge
Container redeployed at
22:50:23(compose-transmit-multi-byte-driver-v7jth9-web-1on mlake). Boot log shows clean migration apply + server start; no morerule_id does not existlines 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 callListVisibleForUseragainst a real DB — but they're gated onTEST_DATABASE_URLand 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 wireTEST_DATABASE_URLinto CI against a scratch DB.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:
Root cause: pasteur's engine fix (
3c840c0—IncludeOptional=falsedefault +trigger_event_idsemantic anchoring) already in main suppresses optionals server-side and leaves mandatory rules with unsatisfied trigger_event_id asIsConditionalrows. The Litigation Builder triplet rendered the full response without applying thefilterByDetailMode('selected')pass the legacy /tools/verfahrensablauf has used since the detail-mode P3 ship.Before (live paliad.de):
After (DOM-injected filter simulating the post-fix render):
Fix layers (one commit per concern):
a815818 — TS calc + Go handler + Builder triplet wiring.
internal/handlers/fristenrechner.goacceptsincludeOptional+triggerEventAnchors, forwards toservices.CalcOptions.frontend/src/client/views/verfahrensablauf-core.tsplumbs both intoCalcParams→ fetch body; addsrulesAwaitingAnchortoDeadlineResponse.frontend/src/client/builder.tshydrateTripletappliesfilterByDetailMode(detailgrad)beforerenderColumnsBody; passesincludeOptional=truewhendetailgrad='all_options'.frontend/src/client/verfahrensablauf.tsaligns the legacy callsite + adds ahasOptionalOptInhelper so per-rulerule:<uuid>=truedeviations still surface in 'selected' mode.frontend/src/client/views/verfahrensablauf-core.test.ts—includeOptional=trueandtriggerEventAnchors={…}round-trip through the request body; empty/default values are omitted so the wire stays minimal.05247d7 — manuscript regen.
exports/gen-deadline-list.pycanonicalises the one-off/tmp/paliad-deadline-export.py(work/head delegation #2572) with a SQL-levelpriority != 'optional'filter;--include-optionalopts back in for an exhaustive dump.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.tsonly inhydrateTriplet(the calc surface, not the mode-state orchestration maxwell's B4 shift owns). No spillover intoscenario_builder_service.goor new handlers.B5 + B6 — Litigation Builder train complete (t-paliad-350, coder=planck)
Branch
mai/planck/coder-b5-b6-train-sharepushed; awaiting head merge to main. This finishes the m/paliad#153 slice train — the Builder is now feature-complete through B6.Commits
d913f4fc30264cc39a6bB5 — what shipped
ScenarioBuilderService.PromoteScenarioruns one Postgres tx — INSERTpaliad.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) → scenariostatus='promoted'+promoted_project_id. Primary top-level proceeding + its spawn-descendants form the one case file; extra standalone proceedings stay in the scenario (reported viaProceedingsSkipped). Planned dates computed via the injectedFristenrechnerService.ListSharedWithMe+GET /api/builder/scenarios/shared,POST /api/builder/scenarios/{id}/promote.B6 — what shipped
.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.grep "from.*fristenrechner-|from.*verfahrensablauf"shows only the keptverfahrensablauf-core/-detail-modethe builder reuses. /tools/fristenrechner + /tools/verfahrensablauf remain 301 redirects.deadlines.party.*keys/block (behaviour-preserving) + added missing DEcal.today; no dupes, full parity.Verification (Playwright, local server vs. TEST_DATABASE_URL)
TestScenarioBuilderPromotepins the transactional cascade (project + 2 deadlines + party created, scenario→promoted with back-ref, read-only-after-promote, re-promote rejected).go build/vet/testclean;bun build+ 227 frontend tests pass.pointer-events:none. (A deep-link auto-load gap for shared scenarios was caught here and fixed.)Sweep verdict (2026-07-29): PARTLY.
This is the design the current
/tools/procedureswas built from, and it is partly built.frontend/src/procedures.tsx:17-19states the slice status directly:All three mode tabs exist (
builder.mode.cold/.event/.akte), but only cold-open is behind them. So two of m's three approaches — "something specific happened" and "from a specific proceeding / case file" — are stubs.Full sweep:
docs/findings-issue-sweep-2026-07-29.md(commit4c39886).