docs(plans): PRD — Procedures Litigation Builder (m/paliad#153)
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.
This commit is contained in:
685
docs/plans/prd-procedures-litigation-planner-2026-05-27.md
Normal file
685
docs/plans/prd-procedures-litigation-planner-2026-05-27.md
Normal file
@@ -0,0 +1,685 @@
|
||||
# PRD — Procedures: Litigation Builder (m/paliad#153)
|
||||
|
||||
**Task:** t-paliad-339
|
||||
**Gitea:** m/paliad#153
|
||||
**Inventor:** edison (shift-1, Opus)
|
||||
**Date:** 2026-05-27
|
||||
**Branch:** `mai/edison/inventor-prd-columnar`
|
||||
**Status:** Draft — DESIGN READY FOR REVIEW. Coder gate held.
|
||||
|
||||
**Builds on (read before extending this PRD):**
|
||||
|
||||
- `docs/design-procedures-workflow-tracker-2026-05-27.md` — atlas's reverted tracker design (m/paliad#152). The anchor+scope idea did not land; understand *why* before re-proposing.
|
||||
- `docs/design-unified-procedural-events-tool-2026-05-27.md` — cronus's U0-U4 catalog, currently live on main @ `ed3c5d1` post-revert. Visual baseline for filter strip + tab control.
|
||||
- `docs/design-deadline-system-revision-2026-05-27.md` — atlas Phase 2 model layer (scenario_flags SSoT, view-mode toggle, per-rule selection chips). Model layer is locked; this PRD is purely surface + new persistence tables.
|
||||
- `docs/design-fristenrechner-overhaul-2026-05-26.md` — cronus 2026-05-26 inventor-pass (Mode A + B + result, shipped via t-paliad-322).
|
||||
|
||||
**Predecessor takeaway (atlas's debrief on #152):**
|
||||
|
||||
> "When the architecture is novel, default to grilling m in prose FIRST. The doc rewrites cost a commit; the bigger cost would have been wasting m's question-batch on the wrong architecture."
|
||||
|
||||
Followed here. This PRD captures the architecture m chose through **20 chip-picker decisions across 5 batches**, not an inventor-first strawman.
|
||||
|
||||
---
|
||||
|
||||
## §0 Premises
|
||||
|
||||
### §0.1 What is `/tools/procedures` today (live, post-revert)
|
||||
|
||||
The current page is cronus's 4-tab catalog (U0-U4, shipped via m/paliad#151):
|
||||
|
||||
- Sticky filter strip (search box + 4 chip rows: Forum / Verfahren / Ereignisart / Partei).
|
||||
- 4 solid tabs: `Verfahren wählen` / `Direkt suchen` / `Geführt` / `Aus Akte`.
|
||||
- Default-active tab = "Verfahren wählen" renders `VerfahrensablaufBody` (the legacy Verfahrensablauf wizard: proceeding picker → perspective + date → 3-step wizard → result in 3-column "Spalten" or single-column "Zeitstrahl").
|
||||
- Other 3 tab panels are stubs (search/wizard/akte never wired in U0-U3).
|
||||
|
||||
m's blocking feedback (verbatim, 2026-05-27 22:18):
|
||||
|
||||
> 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. […] 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 […]. A third one from a specific proceeding / case file where things take place / have taken place.
|
||||
|
||||
And the architecture-shifting follow-ups (2026-05-27 22:35-22:36, mid-grilling):
|
||||
|
||||
> I would prefer to have an interface where not every constellation is in the URL by the way. That seems limiting.
|
||||
> We could just have a litigation builder. Sometimes we build a full scenario with multiple instances etc, sometimes we just want the next step.
|
||||
> we should have ways to save these "litigation constellations" where we save which proceedings we have and which state they are in, which submissions were or were not filed. A small Scenario DB could work, dont you think?
|
||||
|
||||
These three statements upgraded the brief from "redesign a catalog" to "build a Litigation Builder backed by a Scenario DB". The PRD below is shaped by them.
|
||||
|
||||
### §0.2 Locked constraints (m's words, brief in #153)
|
||||
|
||||
- Columnar layout: `proaktiv | court | reaktiv` (perspective-flippable).
|
||||
- Three approaches as entry modes: overview/scenarios, event-triggered, case-file driven.
|
||||
- Filtering across all dimensions + text search.
|
||||
- Optional follow-ups: toggleable, highlightable, with display-count setting.
|
||||
- Modular *where it actually helps* (m: "I don't know — generally does not super apply here." — drop modular as a load-bearing goal).
|
||||
- UPC v1, expand later.
|
||||
|
||||
### §0.3 Live data the builder works against
|
||||
|
||||
Verified 2026-05-27 against `paliad.sequencing_rules` (231 published / 242 total):
|
||||
|
||||
- 110 chained (`parent_id` not null).
|
||||
- 78 trigger-rooted, 4 spawns (cross-PT), 47 court-set, 18 conditional.
|
||||
- ~46 proceeding types total (UPC 35 / DE 5 / EPA 3 / DPMA 3). v1 focuses on UPC.
|
||||
- `paliad.proceeding_types.kind` discriminator (atlas's t-paliad-324) filters non-proceeding rows (phases/side_actions/meta) from the picker.
|
||||
- `paliad.deadlines` carries both `procedural_event_id` and `sequencing_rule_id` → Akte actuals overlay is a direct join.
|
||||
- `paliad.projects.scenario_flags` jsonb (atlas P0) is the SSoT for project-level scenario state; the new `paliad.scenario_proceedings.scenario_flags` mirrors this shape per-proceeding-per-scenario.
|
||||
|
||||
### §0.4 Scope (in / out)
|
||||
|
||||
**In:**
|
||||
|
||||
- Replace `/tools/procedures` with the Litigation Builder.
|
||||
- New `paliad.scenarios` + `paliad.scenario_proceedings` + `paliad.scenario_events` + `paliad.scenario_shares` tables.
|
||||
- Promote-to-project flow (scenario → `paliad.projects` row).
|
||||
- Bidirectional link from `/projects/{id}` (button: "Im Builder öffnen" — exports project state to a builder session).
|
||||
|
||||
**Out (deferred or owned elsewhere):**
|
||||
|
||||
- Calculator (`pkg/litigationplanner.CalculateRule`) — working.
|
||||
- Editorial backfill (curie's t-paliad-333 owns the 7 compound rules + R.109).
|
||||
- `/admin/procedural-events` (editor surface; different audience).
|
||||
- `/projects/{id}` Verlauf / SmartTimeline (per-Akte actuals; sister tool).
|
||||
- youpc.org / Outlook / PDF export.
|
||||
- Multi-jurisdiction expansion (DE/EPA/DPMA) — UPC v1 first.
|
||||
- Cross-proceeding peer triggers (UPC-inf judgment → EPA opp choice deadline) — v1.1.
|
||||
- Multi-user concurrent editing on the same scenario (out of scope; sharing is read-only).
|
||||
|
||||
---
|
||||
|
||||
## §1 Goals
|
||||
|
||||
1. **One canvas, three entry modes.** Unify the 3 approaches into a single Litigation Builder surface. The entry modes (`Übersicht / Ereignis / Aus Akte`) shape the *initial* state of the canvas; once the user is working, the canvas itself is what they interact with.
|
||||
2. **Persisted constellations.** A user can save a "litigation constellation" — multiple parallel proceedings with their flags, filed/skipped/planned event states, dates, and notes — as a named scenario. Scenarios live in the DB (not the URL).
|
||||
3. **Auto-save by default.** No "unsaved changes" modals. The active scenario auto-persists. Anonymous scratch scenarios convert to named ones when the user clicks "Benennen".
|
||||
4. **Promote-to-project.** A scenario can be turned into a real `paliad.projects` row via a 3-step wizard. Procedural shape, placeholder parties, notes, and filed-state all carry over; the user fleshes out client-bound metadata during the wizard.
|
||||
5. **Share read-only with the team.** Each scenario is private by default; explicit "An Team teilen" grants named HLC users read-only access. Original owner stays sole editor.
|
||||
6. **Columnar geometry restored.** The current "Spalten" view (claimant | court | defendant) returns as the canonical render — but now per-proceeding-triplet within a scenario, with perspective ("our side") flippable per proceeding so `proaktiv | court | reaktiv` reads correctly across multi-proceeding constellations.
|
||||
7. **Per-event-card optional horizon.** Each event card on the canvas can dial in how many optional follow-ups to surface. Cards are the unit of optional-display control.
|
||||
|
||||
---
|
||||
|
||||
## §2 User journeys
|
||||
|
||||
### §2.1 Journey A — Cold-open builder ("Übersicht / Scenarios")
|
||||
|
||||
**Persona:** Dr. Becker, senior partner. Friday afternoon. New UPC matter not yet committed; she's briefing a client on Monday on the full procedural shape.
|
||||
|
||||
1. Opens `/tools/procedures`. No `?scenario` param. Cold-open canvas: empty workbench with a "Neues Szenario starten" CTA and a short list of her 5 most-recent scenarios.
|
||||
2. Clicks the CTA → inline picker (Forum chip row → Verfahren chip row → `Hinzufügen`). Picks UPC + `upc.inf.cfi`.
|
||||
3. Canvas now renders one proceeding triplet (`proaktiv | court | reaktiv`). Default perspective is empty (no party selected) — both sides render equally; the perspective radio in the page header sits unset.
|
||||
4. She picks defendant perspective at the page header → triplet flips. The defendant column becomes `proaktiv` (her side); claimant becomes `reaktiv`.
|
||||
5. She adds a second proceeding via `+ Verfahren hinzufügen` at the bottom: EPA `epa.opp.opd`. New triplet stacks below the first. New triplet's perspective defaults to "patentee" inheriting from her client's role across the two; she flips per-proceeding via the triplet header.
|
||||
6. She turns on `with_ccr` on the UPC inf triplet's per-proceeding flag strip. The CCR child triplet auto-expands inline below the parent at the spawn node.
|
||||
7. Auto-save kicks in (debounced 500ms). The page header shows "Gespeichert in Scratch · Benennen".
|
||||
8. She clicks "Benennen", enters "Becker — UPC + EPA defensive". Side panel "Meine Szenarien" updates.
|
||||
9. On Monday she opens the scenario from her recent list, walks the client through it, hits "Als Projekt anlegen" (when the client commits). 3-step wizard fires (§5.4).
|
||||
|
||||
### §2.2 Journey B — Event-triggered lookup ("Ereignis")
|
||||
|
||||
**Persona:** Sandra, paralegal. Today: a Hinweisbeschluss arrived on a CMS queue. She doesn't know yet which Akte it belongs to.
|
||||
|
||||
1. Opens `/tools/procedures`. Picks "Ereignis" entry mode at the top.
|
||||
2. Page-header search box auto-focuses. She types "Hinweis" → universal search drops down: `5 Ereignisse · 1 Szenario · 0 Akten`. Picks the event `upc.inf.cfi.cmo_review` (Antrag CMO-Überprüfung).
|
||||
3. Canvas renders one triplet of `upc.inf.cfi` with the Hinweisbeschluss event card auto-anchored (lime band + `━━ DU BIST HIER ━━` divider above the next-coming events).
|
||||
4. She reads the follow-ups: "Antrag auf CMO-Überprüfung (claimant, R.333.2 · 1 Monat)" and 2 optional follow-ups. The Stichtag input in the page header defaults to today; she leaves it.
|
||||
5. She doesn't save anything — this was a quick lookup. Scratch scenario auto-persists but she doesn't name it; it'll fall off her recent list after a while.
|
||||
6. Later she identifies the matter (HL-2024-001), switches to "Aus Akte" mode, and continues there.
|
||||
|
||||
### §2.3 Journey C — Case-file driven ("Aus Akte")
|
||||
|
||||
**Persona:** Anna, senior associate. Working on HL-2024-001 (UPC infringement). The client just confirmed they want to file a CCR.
|
||||
|
||||
1. Opens `/tools/procedures`. Page-header Akte picker shows recent projects; she picks HL-2024-001.
|
||||
2. Page header auto-fills: proceeding = `upc.inf.cfi`, perspective = defendant (from `projects.our_side`), scenario_flags = `{with_ccr: false}` (current state).
|
||||
3. Builder loads: one `upc.inf.cfi` triplet, perspective-flipped. Event cards overlay actuals from `paliad.deadlines` — `Klageerhebung` is filed (2026-01-15), `Klageerwiderung` is planned (2026-04-01, computed), others are planned.
|
||||
4. She turns on `with_ccr` on the triplet's flag strip. The CCR child triplet expands inline. **Crucially:** the scenario is *project-backed* — the flag write also patches `projects.scenario_flags` (via existing `PATCH /api/projects/{id}/scenario-flags` from atlas P0). When she walks away, the project's deadlines + flags reflect the builder's state.
|
||||
5. She marks the `Widerklage auf Nichtigkeit` event card as "filed" with today's date. Builder writes a `paliad.deadlines` row with `status='done'` + `completed_at=today`, audit_reason "via Litigation Builder". Project's Verlauf reflects this.
|
||||
6. The CCR child triplet's `Antrag Patentänderung (R.30)` event card surfaces. She marks it "planned" and ticks the per-card optional horizon to "+2" → 2 more optional R.30-adjacent rules surface.
|
||||
7. Exit: she closes the tab. Project state persists in `paliad.projects` + `paliad.deadlines` as before; the scenario row tracks the builder-session view (so when she returns, the canvas state is restored — including her per-card optional-horizon picks).
|
||||
|
||||
### §2.4 Journey D — Promote scratch to a real project
|
||||
|
||||
**Persona:** Dr. Becker, follow-up from Journey A. The client committed; she wants to convert the scenario into a real matter.
|
||||
|
||||
1. With "Becker — UPC + EPA defensive" loaded, she clicks "Als Projekt anlegen" in the page header.
|
||||
2. **Wizard step 1: Bestätigen.** Read-only summary of what's about to be promoted: 2 proceedings (UPC inf + EPA opp), CCR child, 3 scenario flags set, 0 events filed, 5 events planned, 2 notes. "Weiter".
|
||||
3. **Wizard step 2: Parteien ergänzen.** Each proceeding's parties section shows whatever placeholder names she sketched in the scenario ("Klg X" / "Bekl Y"). She edits each into the real names. (Per m's Q11 pick — full carry — placeholder strings come in; the wizard's job is to clean them.)
|
||||
4. **Wizard step 3: Akte-Metadaten.** Case number, client, litigation parent project (optional), our_side (auto-set from the scenario's primary triplet), team selection. "Anlegen".
|
||||
5. New `paliad.projects` row written with `origin_scenario_id = <scenario.id>`. Scenario row's `status` flips to `promoted`, `promoted_project_id` points back. Builder navigates to `/projects/<new-id>`.
|
||||
6. The scenario stays read-only in her "Meine Szenarien" list under "Promoted", reachable for historical reference (cf. "this is what we planned at briefing time").
|
||||
|
||||
### §2.5 Journey E — Share a scenario with a colleague
|
||||
|
||||
**Persona:** Anna shares the HL-2024-001 builder session with Dr. Becker (her supervising partner) for review before committing to the CCR strategy.
|
||||
|
||||
1. Anna opens the scenario, clicks "Teilen" in the page header.
|
||||
2. Side panel slides in with a user-picker (HLC user search). She picks "Dr. Becker", clicks "Schreibgeschützt teilen".
|
||||
3. `paliad.scenario_shares` row written. Anna remains sole editor.
|
||||
4. Dr. Becker opens the tool. Her side panel "Meine Szenarien" has a new bucket "Geteilt mit mir"; Anna's scenario is listed. She opens it: canvas renders the same view but every mutating affordance (add proceeding, flag toggle, file/skip, promote, share) is disabled. Watermark: "Geteilt von Anna · schreibgeschützt".
|
||||
5. Becker reads, drops Anna a note via existing comment infrastructure (out of scope — separate ticket). Decision made out-of-band. Anna proceeds.
|
||||
|
||||
---
|
||||
|
||||
## §3 The canvas shape
|
||||
|
||||
### §3.1 ASCII sketch
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Paliad · Verfahren & Fristen — Litigation Builder [Mein Konto ▾] │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ Szenario: [Becker — UPC + EPA def. ▼] Gespeichert ✓ · [Benennen] [Teilen] [Als Projekt] │
|
||||
│ Akte: [— ohne — ▼] Stichtag: [2026-04-01] │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ Filter: [🔍 Klageerwiderung, Hinweis, HL-2024… ] │
|
||||
│ Forum [● UPC] [DE] [EPA] [DPMA] Verfahren [● upc.inf.cfi …] │
|
||||
│ Partei [Klg] [● Bekl] Ereignisart [filing] [hearing] [decision] │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ Einstieg: [ Übersicht ● ][ Ereignis ○ ][ Aus Akte ○ ] │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ upc.inf.cfi · Verletzungsverfahren UPC Bekl-Sicht [▾] [Detailgrad: Gewählt ▾]│
|
||||
│ │ Optionen: ☑ with_ccr ☐ with_amend ☐ with_cci [─][×] │
|
||||
│ ├─────────────────┬────────────────────┬─────────────────────────────────────────┤
|
||||
│ │ Proaktiv (Bekl) │ Gericht │ Reaktiv (Klg) │
|
||||
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||||
│ │ │ Klageerw. │ │ │ │ Klageerh. │ │
|
||||
│ │ │ R.23 │ │ │ │ R.13 │ │
|
||||
│ │ │ planned │ │ │ │ filed │ │
|
||||
│ │ │ 2026-04-01 │ │ │ │ 2026-01-15 │ │
|
||||
│ │ │ +3 Optionen ▾│ │ │ │ │ │
|
||||
│ │ └─────────────┘ │ │ └─────────────┘ │
|
||||
│ │ │ ┌──────────────┐ │ │
|
||||
│ │ │ │ Mündl. Verh. │ │ │
|
||||
│ │ │ │ planned │ │ │
|
||||
│ │ │ │ [Gericht] │ │ │
|
||||
│ │ │ └──────────────┘ │ │
|
||||
│ │ ━━━━━━━━━ DU BIST HIER (Klageerwiderung) ━━━━━━━━━ │
|
||||
│ └─────────────────┴────────────────────┴─────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── (spawn child) upc.ccr.cfi · Widerklage auf Nichtigkeit Klg-Sicht [▾] ───────┐│
|
||||
│ │ Optionen: ☐ with_amend [─][×]││
|
||||
│ ├────────────────────┬─────────────────┬───────────────────────────────────────┐ ││
|
||||
│ │ Proaktiv (Klg) │ Gericht │ Reaktiv (Bekl) │ ││
|
||||
│ │ ┌─────────────┐ │ │ │ ││
|
||||
│ │ │ CCR-Antrag │ │ │ │ ││
|
||||
│ │ │ R.49 │ │ │ │ ││
|
||||
│ │ │ planned │ │ │ │ ││
|
||||
│ │ └─────────────┘ │ │ │ ││
|
||||
│ └────────────────────┴─────────────────┴───────────────────────────────────────┘ ││
|
||||
│ │
|
||||
│ ┌─ epa.opp.opd · Einspruchsverfahren EPA PatInh-Sicht [▾] [Detailgrad: Gewählt ▾]│
|
||||
│ │ Optionen: (keine flags für EPA Opp) [─][×] │
|
||||
│ ├─────────────────┬────────────────────┬─────────────────────────────────────────┤
|
||||
│ │ Proaktiv │ EPA │ Reaktiv (Einsprechende) │
|
||||
│ │ ┌─────────────┐ │ │ │
|
||||
│ │ │ Erwiderung │ │ │ │
|
||||
│ │ │ R.79(1) EPÜ │ │ │ │
|
||||
│ │ │ planned │ │ │ │
|
||||
│ │ └─────────────┘ │ │ │
|
||||
│ └─────────────────┴────────────────────┴─────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [ + Verfahren hinzufügen ] │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Side panel (collapsible, right-edge):
|
||||
┌──── Meine Szenarien ────┐
|
||||
│ ● Aktiv │
|
||||
│ ▸ Becker — UPC+EPA def │ ← current
|
||||
│ ▸ Test-CCR-Patent-X │
|
||||
│ ○ Geteilt mit mir │
|
||||
│ ▸ Becker UPC ply │
|
||||
│ ○ Promoted │
|
||||
│ ▸ HL-2023-118 │
|
||||
│ ○ Archiviert (3) │
|
||||
│ [+ Neues Szenario] │
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
### §3.2 What each element does
|
||||
|
||||
| Element | Read | Write | Persists in |
|
||||
|---|---|---|---|
|
||||
| Page-header scenario picker | Current `scenarios.id` + `name` | Switch scenarios | URL `?scenario=<id>` + DB |
|
||||
| `[Benennen]` button | Anonymous → named | `scenarios.name`, `status='active'` | DB |
|
||||
| `[Teilen]` button | — | `scenario_shares` row(s) | DB |
|
||||
| `[Als Projekt]` button | — | Opens promote wizard | (wizard → DB on commit) |
|
||||
| Akte picker | User's projects | Loads project state into builder | URL `?project=<id>` + DB |
|
||||
| Stichtag input | Scenario-level default | `scenarios.stichtag` | DB |
|
||||
| Filter strip (search + chips) | Free-text + dimension filters | UI state | URL `?q`, `?forum`, … per-mode |
|
||||
| Einstieg mode radio | Current entry mode | Resets filter strip on change | URL `?mode=` |
|
||||
| Triplet header (jurisdiction badge + name + perspective + Detailgrad) | `scenario_proceedings.{primary_party, detailgrad}` | Edit | DB |
|
||||
| Triplet flag strip | `scenario_proceedings.scenario_flags` | Toggle flags | DB |
|
||||
| Event card (state, date, notes, optional-horizon) | `scenario_events.*` | Edit per-card | DB |
|
||||
| `+ Verfahren hinzufügen` | — | New `scenario_proceedings` row | DB |
|
||||
| Side panel | User's scenarios + shared scenarios | Switch + create + archive | DB |
|
||||
|
||||
### §3.3 Columns: `proaktiv | court | reaktiv`
|
||||
|
||||
The 3-column layout returns as the canonical desktop shape. Per m's locked constraint (and brief #153), it is a **stance grouping**, not a sequence anchor — time flows top-to-bottom (chronological), columns express *who is acting*.
|
||||
|
||||
- **Proaktiv**: the column for events the active perspective's party initiates (their `primary_party` matches the event's `primary_party`).
|
||||
- **Court**: court-set events (`is_court_set=true`), neutral column.
|
||||
- **Reaktiv**: the column for events the opposing party initiates.
|
||||
|
||||
The perspective is per-proceeding (per-triplet, via `scenario_proceedings.primary_party`). When no perspective is set (`null`), both party columns render equally with their natural party labels (Klg / Bekl), not Proaktiv / Reaktiv. This means kontextfrei browsing reads as "claimant column | court | defendant column" until the user picks a side.
|
||||
|
||||
This addresses m's reverted-design bug #3 verbatim: "Proaktiv/Gericht/Reaktiv columns are a stance grouping, not a sequence anchor." Time = vertical. Stance = horizontal. The triplet is the unit; multiple proceedings stack vertically.
|
||||
|
||||
### §3.4 Event card anatomy
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ Klageerwiderung │ ← event name (procedural_event.name)
|
||||
│ R.23 │ ← rule code
|
||||
│ planned │ ← state: planned / filed / skipped
|
||||
│ 2026-04-01 │ ← date (computed for planned, actual for filed)
|
||||
│ +3 Optionen ▾ │ ← per-card optional horizon (only when card has optionals)
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
State machine (m's Q10 pick — 3-state):
|
||||
|
||||
- `planned` (default): future event, date is computed from anchor + duration_value + duration_unit. Click → choose `filed` or `skipped`.
|
||||
- `filed`: past event, `actual_date` is set (defaults to computed, user can override). Visual: ✓ checkmark, slightly muted "past" tone.
|
||||
- `skipped`: user chose not to file. Visual: strikethrough text + optional `skip_reason` (textarea). Optional rules are commonly skipped without rationale; mandatory rules with `skipped` state flag the scenario as "non-standard" but don't block.
|
||||
|
||||
No `overdue` state — user does the date arithmetic by eye against today. (Mandatory cards rendered in red when `actual_date < today AND state=planned` is a **render hint**, not a stored state.)
|
||||
|
||||
**Per-card optional horizon (m's Q4 pick).** Each card with children at `priority IN ('optional','recommended-skip-by-default')` carries a chip `+N Optionen ▾`. Default N=0 (hidden). Clicking opens an inline list of the optional children with `+`/`-` controls to surface/hide them on the canvas. Per-card horizon persists as `scenario_events.horizon_optional int`.
|
||||
|
||||
Filed-state cards persist the date in `scenario_events.actual_date date`. The card's notes field (textarea, lazy-loaded) lives in `scenario_events.notes text`.
|
||||
|
||||
### §3.5 Court-set events
|
||||
|
||||
`is_court_set=true` rules don't compute a date until the court picks one. Card renders with `[Gericht]` badge in place of the date and a small "Datum eintragen" affordance. Clicking `filed` opens a date picker (date is required for `filed` state when `is_court_set=true` — the user is asserting "the court set this date").
|
||||
|
||||
Downstream events that anchor on a court-set event render their dates as `[abhängig von <event>]` until the court date is filed, then auto-recompute.
|
||||
|
||||
### §3.6 Spawn (child) proceedings
|
||||
|
||||
When a triplet has a `with_<flag>` enabled and the flag's gating rule has `is_spawn=true`, the child proceeding (e.g. `upc.ccr.cfi` for `with_ccr` on `upc.inf.cfi`) renders inline as a child triplet **immediately below the parent triplet** in the canvas stack — visually nested via the spawn note in the parent triplet's header band.
|
||||
|
||||
`scenario_proceedings.parent_scenario_proceeding_id` FK self-references for the nesting; `scenario_proceedings.spawn_anchor_event_id` points at the gating sequencing_rule so the UI knows where in the parent the spawn happened.
|
||||
|
||||
The child triplet has its own perspective, scenario flags, Stichtag override, Detailgrad. It can itself spawn (depth N supported; today's data is 2-deep at most).
|
||||
|
||||
Cross-proceeding peer triggers (`upc.inf judgment → epa.opp choice deadline`) are **out of scope for v1** (m's Q14 pick). v1 ships independent triplets stacked vertically; the user mentally tracks cross-dependencies. A future `scenario_event_links` table is the path to peer triggers in v1.1.
|
||||
|
||||
---
|
||||
|
||||
## §4 Hard decisions table — m's 20 picks
|
||||
|
||||
| # | Topic | Pick | Locks |
|
||||
|---|---|---|---|
|
||||
| Q1 | Modular meaning | "doesn't super apply" — drop modular as a load-bearing goal | §0.2 |
|
||||
| Q2 | Tab state semantics | Shared anchor + Akte across modes; filters reset per mode | §3.1, §3.2, §6 |
|
||||
| Q3 | Case-file integration | Page-header Akte picker, persistent across modes | §3.1, §3.2, §2.3 |
|
||||
| Q4 | Optional-display horizon | Per-event-card | §3.4 |
|
||||
| Q5 | Builder shape | Unified builder, 3 entry modes (cold-open / event-triggered / Akte) | §0, §1, §2, §3 |
|
||||
| Q6 | Scenario↔project relationship | Separate `paliad.scenarios` table + promote-to-project action | §5, §2.4 |
|
||||
| Q7 | Scenario contents | Multi-proceeding constellation per scenario | §3, §5 |
|
||||
| Q8 | Save model | Auto-save active scenario + "Meine Szenarien" list | §1, §3, §6.4 |
|
||||
| Q9 | Multi-proceeding render | Vertical stacked column-triplets | §3 |
|
||||
| Q10 | Per-event state | 3-state: planned / filed / skipped (no `overdue` state) | §3.4 |
|
||||
| Q11 | Promote-to-project carry | Everything (incl. placeholder parties + free-form notes) | §2.4, §5.4 |
|
||||
| Q12 | Sharing model | Private by default + explicit team-share (read-only) | §1, §5, §2.5 |
|
||||
| Q13 | Scenario flags placement | Per-proceeding (each triplet owns its `scenario_flags`) | §5.1 |
|
||||
| Q14 | Cross-proceeding peer triggers | Out of scope for v1 (defer to v1.1) | §3.6, §7 |
|
||||
| Q15 | Perspective scope | Per-proceeding (each triplet has its own `primary_party`) | §3.3, §5.1 |
|
||||
| Q16 | Add-proceeding flow | `+ Verfahren hinzufügen` button below the last triplet, inline picker | §3, §3.1 |
|
||||
| Q17 | Cold-open canvas | Empty canvas + "Neues Szenario" CTA + recent-list | §2.1, §3 |
|
||||
| Q18 | Search scope | Universal: events + scenarios + Akten, scoped by result type | §3.1, §6 |
|
||||
| Q19 | Promote-to-project flow | 3-step wizard (Bestätigen → Parteien ergänzen → Akte-Metadaten) | §2.4, §5.4 |
|
||||
| Q20 | Mobile treatment | Desktop v1, mobile basic-read (mutating actions prompt "Auf größerem Bildschirm öffnen") | §3, §7 |
|
||||
|
||||
### §4.1 Divergences from inventor recommendations
|
||||
|
||||
Three picks diverged from my recommendation. Captured here so future readers (m, the coder) see the *current* design, not the strawman.
|
||||
|
||||
- **Q1 — Modular.** Inventor recommended "plug-in widgets". m: "I don't know — generally does not super apply here." Modular is dropped as a goal; the natural decomposition (BuilderCanvas → ProceedingTriplet → EventCard → ScenarioListPanel → PromoteWizard) is documented in §6.2 as build hygiene, not as a load-bearing constraint.
|
||||
- **Q10 — Event state.** Inventor recommended 4-state (planned / filed / skipped / overdue). m picked 3-state — no `overdue` enum. Rationale (interpreted): `overdue` is derived from `date < today AND state=planned`, not stored; this avoids stale state when the date is edited.
|
||||
- **Q11 — Promote carry.** Inventor recommended carrying procedural shape + flags + filed-state + notes but **not** placeholder parties/case_number/billing. m picked "everything carries" — placeholder parties come in. Mitigation: Q19's 3-step wizard's step 2 (Parteien ergänzen) gives the user a chance to clean placeholders before commit, so the safety net m wanted on Q11 is folded into Q19.
|
||||
|
||||
### §4.2 Inventor picks not formally asked
|
||||
|
||||
A few decisions are inventor-set because they're either: (a) implementation details that don't change the architecture, or (b) clean defaults that match existing patterns. Listed here so they're visible; m can flag any.
|
||||
|
||||
- **Detailgrad ("Gewählt" / "Alle Optionen") scope**: per-proceeding (matches today's Verfahrensablauf pattern). State in `scenario_proceedings.detailgrad`.
|
||||
- **Akte picker shape**: flat dropdown sorted by recently-viewed first, with a typeahead filter for case numbers/names. Same shape as today's project picker on /agenda.
|
||||
- **Notes**: per-event-card (textarea on each card, lazy-loaded). Scenario-level notes also exist (`scenarios.notes text`) for cross-cutting commentary.
|
||||
- **Read-only shared state UI**: every mutating affordance is disabled (greyed, no click handlers). Watermark "Geteilt von <X> · schreibgeschützt" at the top of the canvas. No "Fork to my workspace" affordance in v1.
|
||||
- **URL contract**: minimal, view-state only — `?scenario=<id>&mode=<entry>&event=<sequencing_rule_id>` (deep-link to a specific anchor). Filter pills + chip state get URL params *per active entry mode* but explicitly NOT the constellation data (per m's "not every constellation in URL" guidance). The constellation lives in `paliad.scenario_*` tables.
|
||||
- **Auto-save granularity**: debounced 500ms on every change. Indicator near scenario name: `Gespeichert ✓` (last successful save < 5s ago), `Speichert…` (in flight), `Letzte Speicherung fehlgeschlagen — erneut versuchen` (on error).
|
||||
- **Soft delete**: archived scenarios stay in DB with `status='archived'`. No hard delete in v1.
|
||||
- **Audit**: no audit log on scenario edits (they're exploratory). Audit on promote-to-project goes via the existing `projects.audit_log`.
|
||||
- **Concurrent editing**: single-editor model. Owner is sole editor; shares are read-only. No locking / merge conflict UI needed in v1.
|
||||
- **Bilingual**: German primary, English via existing `i18n.ts`. Scenario names: user-chosen, any language. Skip reasons + notes: free-text, any language.
|
||||
|
||||
---
|
||||
|
||||
## §5 Data model deltas
|
||||
|
||||
All new tables live in `paliad.*` schema, alongside existing `paliad.projects` / `paliad.deadlines` / `paliad.sequencing_rules`.
|
||||
|
||||
### §5.1 New tables
|
||||
|
||||
```sql
|
||||
-- Scenario header. One row per saved scenario (named or scratch).
|
||||
CREATE TABLE paliad.scenarios (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
name text NOT NULL DEFAULT 'Unbenanntes Szenario',
|
||||
status text NOT NULL DEFAULT 'active'
|
||||
CHECK (status IN ('active','archived','promoted')),
|
||||
origin_project_id uuid NULL REFERENCES paliad.projects(id) ON DELETE SET NULL,
|
||||
-- set when scenario was exported from a project
|
||||
promoted_project_id uuid NULL REFERENCES paliad.projects(id) ON DELETE SET NULL,
|
||||
-- set when scenario was promoted to a project
|
||||
stichtag date NULL,
|
||||
-- scenario-level default Stichtag; per-triplet overrides take precedence
|
||||
notes text NULL,
|
||||
-- free-form scenario-level commentary
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX scenarios_owner_status_idx ON paliad.scenarios(owner_id, status);
|
||||
CREATE INDEX scenarios_updated_idx ON paliad.scenarios(owner_id, updated_at DESC);
|
||||
|
||||
-- One row per proceeding inside a scenario. Multiple per scenario for
|
||||
-- multi-proceeding constellations. parent_scenario_proceeding_id self-refs
|
||||
-- for spawned children (CCR child of UPC inf etc.).
|
||||
CREATE TABLE paliad.scenario_proceedings (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
scenario_id uuid NOT NULL REFERENCES paliad.scenarios(id) ON DELETE CASCADE,
|
||||
proceeding_type_id uuid NOT NULL REFERENCES paliad.proceeding_types(id),
|
||||
primary_party text NULL
|
||||
CHECK (primary_party IN ('claimant','defendant')),
|
||||
-- per-proceeding perspective; null = no perspective picked yet
|
||||
scenario_flags jsonb NOT NULL DEFAULT '{}'::jsonb,
|
||||
-- per-proceeding flags: {with_ccr: true, with_amend: false, …}
|
||||
parent_scenario_proceeding_id uuid NULL REFERENCES paliad.scenario_proceedings(id) ON DELETE CASCADE,
|
||||
-- self-ref for spawned children (CCR child of UPC inf etc.)
|
||||
spawn_anchor_event_id uuid NULL REFERENCES paliad.sequencing_rules(id),
|
||||
-- which rule of the parent caused this spawn (for UI placement)
|
||||
ordinal int NOT NULL DEFAULT 0,
|
||||
-- stack order on canvas (top to bottom)
|
||||
stichtag date NULL,
|
||||
-- per-proceeding Stichtag override; falls back to scenarios.stichtag
|
||||
detailgrad text NOT NULL DEFAULT 'selected'
|
||||
CHECK (detailgrad IN ('selected','all_options')),
|
||||
appeal_target text NULL,
|
||||
-- applies_to_target for appeal proceedings; null for non-appeal triplets
|
||||
collapsed boolean NOT NULL DEFAULT false,
|
||||
-- user-collapsed triplet header (UI state)
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX scenario_proceedings_scenario_idx ON paliad.scenario_proceedings(scenario_id, ordinal);
|
||||
CREATE INDEX scenario_proceedings_parent_idx ON paliad.scenario_proceedings(parent_scenario_proceeding_id);
|
||||
|
||||
-- One row per event card on the canvas. Captures the card's state +
|
||||
-- per-card attributes (filed date, skip reason, notes, optional horizon).
|
||||
-- Most cards are sequencing-rule-backed; free-form events have a null
|
||||
-- sequencing_rule_id and a non-null procedural_event_id (or text label).
|
||||
CREATE TABLE paliad.scenario_events (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
scenario_proceeding_id uuid NOT NULL REFERENCES paliad.scenario_proceedings(id) ON DELETE CASCADE,
|
||||
sequencing_rule_id uuid NULL REFERENCES paliad.sequencing_rules(id),
|
||||
procedural_event_id uuid NULL REFERENCES paliad.procedural_events(id),
|
||||
-- one of {sequencing_rule_id, procedural_event_id, custom_label} must be set
|
||||
custom_label text NULL,
|
||||
-- free-form event name when neither sequencing_rule nor procedural_event apply
|
||||
state text NOT NULL DEFAULT 'planned'
|
||||
CHECK (state IN ('planned','filed','skipped')),
|
||||
actual_date date NULL,
|
||||
-- set when state='filed'; can also be set for state='planned' (court-set override)
|
||||
skip_reason text NULL,
|
||||
-- optional rationale when state='skipped'
|
||||
notes text NULL,
|
||||
-- per-card free-form
|
||||
horizon_optional int NOT NULL DEFAULT 0,
|
||||
-- per-card "show N more optionals" affordance
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (scenario_proceeding_id, sequencing_rule_id) WHERE sequencing_rule_id IS NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX scenario_events_proceeding_idx ON paliad.scenario_events(scenario_proceeding_id);
|
||||
|
||||
-- Read-only team shares. Owner is sole editor; shares grant view-only.
|
||||
CREATE TABLE paliad.scenario_shares (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
scenario_id uuid NOT NULL REFERENCES paliad.scenarios(id) ON DELETE CASCADE,
|
||||
shared_with_user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
created_by uuid NOT NULL REFERENCES auth.users(id),
|
||||
UNIQUE (scenario_id, shared_with_user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX scenario_shares_user_idx ON paliad.scenario_shares(shared_with_user_id);
|
||||
```
|
||||
|
||||
### §5.2 Additions to existing tables
|
||||
|
||||
```sql
|
||||
-- One nullable FK on paliad.projects to track which scenario spawned this
|
||||
-- project (set on promote-to-project). Auditable origin trail.
|
||||
ALTER TABLE paliad.projects
|
||||
ADD COLUMN origin_scenario_id uuid NULL
|
||||
REFERENCES paliad.scenarios(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX projects_origin_scenario_idx ON paliad.projects(origin_scenario_id)
|
||||
WHERE origin_scenario_id IS NOT NULL;
|
||||
```
|
||||
|
||||
No other changes to existing schema. `paliad.deadlines` continues to be the authoritative source for project-bound actuals; the builder writes to `paliad.deadlines` (not `scenario_events`) when working in Akte mode against a project-backed scenario.
|
||||
|
||||
### §5.3 RLS
|
||||
|
||||
Same pattern as existing `paliad.projects`:
|
||||
|
||||
- `scenarios` readable by `owner_id` OR by users with a matching `scenario_shares.shared_with_user_id` row.
|
||||
- `scenarios` writable only by `owner_id` (and only when `status != 'promoted'`).
|
||||
- `scenario_proceedings` + `scenario_events` cascade from scenario visibility.
|
||||
- `scenario_shares` readable by `shared_with_user_id` or `created_by`; writable only by the scenario owner.
|
||||
|
||||
Helper function `paliad.can_see_scenario(scenario_id)` mirrors the existing `paliad.can_see_project(project_id)` shape.
|
||||
|
||||
### §5.4 Promote-to-project: data flow
|
||||
|
||||
```
|
||||
[Wizard step 1: Bestätigen]
|
||||
Read: scenarios + scenario_proceedings + scenario_events
|
||||
Action: none (read-only summary)
|
||||
|
||||
[Wizard step 2: Parteien ergänzen]
|
||||
Read: scenario_proceedings.scenario_flags (for hints about placeholder party names)
|
||||
Action: builds an in-memory parties payload (per proceeding, per role)
|
||||
|
||||
[Wizard step 3: Akte-Metadaten]
|
||||
Read: user's clients + litigations + project tree (existing /projects API)
|
||||
Action: builds an in-memory project metadata payload
|
||||
|
||||
[Commit]
|
||||
Transaction:
|
||||
1. INSERT into paliad.projects (carrying step-2 + step-3 payloads, + scenario notes)
|
||||
SET origin_scenario_id = <scenario.id>
|
||||
2. INSERT into paliad.project_parties from step-2 payload
|
||||
3. For each scenario_proceeding (depth-first, parent before child):
|
||||
a. INSERT scenario_flags as projects.scenario_flags (parent-level only;
|
||||
children become sub-projects via parent_project_id)
|
||||
b. For each filed scenario_event: INSERT paliad.deadlines row with
|
||||
status='done', completed_at=actual_date, audit_reason='via Litigation Builder promotion'
|
||||
c. For each planned scenario_event: INSERT paliad.deadlines row with
|
||||
status='open', due_date=computed (or actual_date override)
|
||||
d. Skipped events: not inserted (no deadline row)
|
||||
4. UPDATE paliad.scenarios SET status='promoted', promoted_project_id=<new>
|
||||
5. Navigate to /projects/<new>
|
||||
```
|
||||
|
||||
The deadlines write uses existing `POST /api/projects/{id}/deadlines/bulk` semantics under the hood — no new bulk-deadline-from-scenario endpoint needed.
|
||||
|
||||
---
|
||||
|
||||
## §6 Modular boundaries (light)
|
||||
|
||||
m said modular "doesn't super apply" — dropped as a load-bearing goal. The natural decomposition below is build-hygiene documentation, not a constraint the coder must enforce.
|
||||
|
||||
### §6.1 Front-end components
|
||||
|
||||
| Component | File | Responsibility |
|
||||
|---|---|---|
|
||||
| `BuilderCanvas` | `frontend/src/components/BuilderCanvas.tsx` | Root render of the builder. Receives the active scenario, renders triplet stack + cold-open empty state |
|
||||
| `ProceedingTriplet` | `frontend/src/components/ProceedingTriplet.tsx` | One proceeding's render: header strip (jurisdiction + name + perspective + Detailgrad + collapse + remove) + flag strip + 3 columns + spawn child triplets recursively |
|
||||
| `EventCard` | `frontend/src/components/EventCard.tsx` | One card in a column lane. State / date / optional-horizon / notes affordances |
|
||||
| `ScenarioFlagsStrip` | `frontend/src/components/ScenarioFlagsStrip.tsx` | Per-triplet flag toggles. Reads scenario_flag_catalog, applies to scenario_proceedings.scenario_flags |
|
||||
| `AddProceedingPicker` | `frontend/src/components/AddProceedingPicker.tsx` | Inline picker triggered by `+ Verfahren hinzufügen`. Forum chip row → Verfahren chip row → `Hinzufügen` |
|
||||
| `ScenarioListPanel` | `frontend/src/components/ScenarioListPanel.tsx` | Side panel: Aktiv / Geteilt / Promoted / Archiviert buckets + new-scenario CTA |
|
||||
| `PromoteToProjectWizard` | `frontend/src/components/PromoteToProjectWizard.tsx` | 3-step modal: Bestätigen / Parteien / Metadaten |
|
||||
| `PageHeaderControls` | `frontend/src/components/PageHeaderControls.tsx` | Scenario picker + Benennen/Teilen/Promote buttons + Akte picker + Stichtag input |
|
||||
| `EntryModeChrome` | `frontend/src/components/EntryModeChrome.tsx` | Cold-open / event-triggered / Akte mode radio; ephemeral UI affordance that fades into canvas state |
|
||||
|
||||
### §6.2 Client TS files
|
||||
|
||||
Mirror the React-ish component split:
|
||||
|
||||
- `frontend/src/client/builder.ts` — root orchestrator (auto-save loop, URL state, mode routing, scenario fetch)
|
||||
- `frontend/src/client/builder-scenario.ts` — scenario CRUD against `/api/scenarios`
|
||||
- `frontend/src/client/builder-event-card.ts` — per-card state machine + optional-horizon control
|
||||
- `frontend/src/client/builder-promote-wizard.ts` — 3-step wizard state machine
|
||||
- `frontend/src/client/builder-search.ts` — universal search (events + scenarios + Akten)
|
||||
- `frontend/src/client/builder-shares.ts` — share-with-team UI
|
||||
|
||||
### §6.3 Backend services + routes
|
||||
|
||||
| Service | File | Endpoints |
|
||||
|---|---|---|
|
||||
| `ScenarioService` | `internal/services/scenario_service.go` | List / Get / Create / Update / Archive / Promote |
|
||||
| `ScenarioProceedingService` | `internal/services/scenario_proceeding_service.go` | Add / Remove / Update (flags, perspective, ordinal, detailgrad) |
|
||||
| `ScenarioEventService` | `internal/services/scenario_event_service.go` | List / Update state / Set date / Set notes / Set horizon |
|
||||
| `ScenarioShareService` | `internal/services/scenario_share_service.go` | List / Add / Remove shares |
|
||||
| `ScenarioPromoteService` | `internal/services/scenario_promote_service.go` | Wizard-driven transactional promote |
|
||||
|
||||
Routes (added under existing API namespace):
|
||||
|
||||
```
|
||||
GET /api/scenarios — list user's scenarios (filtered by status)
|
||||
POST /api/scenarios — create new scenario
|
||||
GET /api/scenarios/{id} — get scenario + proceedings + events (deep)
|
||||
PATCH /api/scenarios/{id} — update name / stichtag / notes / status
|
||||
DELETE /api/scenarios/{id} — archive (soft delete; status='archived')
|
||||
POST /api/scenarios/{id}/proceedings — add proceeding to scenario
|
||||
PATCH /api/scenarios/{id}/proceedings/{pid} — update flags / perspective / ordinal / detailgrad
|
||||
DELETE /api/scenarios/{id}/proceedings/{pid} — remove proceeding (cascades to events)
|
||||
PATCH /api/scenarios/{id}/events/{eid} — update state / date / notes / horizon
|
||||
POST /api/scenarios/{id}/shares — share with user (read-only)
|
||||
DELETE /api/scenarios/{id}/shares/{sid} — revoke share
|
||||
POST /api/scenarios/{id}/promote — promote to project (3-step wizard payload)
|
||||
POST /api/scenarios/from-project/{project_id} — export project to a new scenario (what-if)
|
||||
GET /api/search — universal search (events + scenarios + Akten)
|
||||
```
|
||||
|
||||
Existing endpoints used unchanged:
|
||||
|
||||
- `GET /api/tools/fristenrechner/search?kind=events` — for the events corpus.
|
||||
- `GET /api/projects` — Akte picker source.
|
||||
- `POST /api/projects/{id}/deadlines/bulk` — promotion writes deadlines through this.
|
||||
- `PATCH /api/projects/{id}/scenario-flags` — Akte-mode flag sync.
|
||||
|
||||
---
|
||||
|
||||
## §7 Migration plan from current live shape
|
||||
|
||||
Current live (`/tools/procedures` on main @ `ed3c5d1`) = cronus's U0-U4 4-tab catalog. Migration is a 6-slice train, every slice ships visibly. No feature flag (m's pattern preference per #152 Q7).
|
||||
|
||||
### §7.1 Slice train
|
||||
|
||||
| Slice | What ships | DB | Visible to user |
|
||||
|---|---|---|---|
|
||||
| **B0 — Scenario DB foundation** | New tables (scenarios + scenario_proceedings + scenario_events + scenario_shares) + RLS + minimal API (list / create / get). Scenarios writable from a developer-only test route at first. | Mig #N (new tables + RLS + `paliad.projects.origin_scenario_id`) | No user-visible change. |
|
||||
| **B1 — Builder shell + cold-open mode** | New `/tools/procedures` page replaces the 4-tab catalog. Renders: page header (scenario picker + Akte picker + Stichtag + search), entry-mode radio (cold-open active), filter strip, empty canvas + "Neues Szenario starten" CTA + recent list. Add-proceeding picker works; first triplet renders with the existing Verfahrensablauf-core calc. Auto-save active scenario. Side panel "Meine Szenarien" with Aktiv bucket only. | — | New page visible. Single triplet works end-to-end. |
|
||||
| **B2 — Multi-triplet + spawn nesting + per-event state** | Vertical multi-triplet stack with `+ Verfahren hinzufügen`. Per-triplet perspective + flag strip. Spawn child triplets render inline. Event cards get the 3-state machine (planned/filed/skipped) + date editor + per-card optional horizon chip. Page-header Stichtag drives default dates. | — | Full scenario builder works without Akte integration. |
|
||||
| **B3 — Event-triggered mode + universal search** | "Ereignis" entry mode wires the search box to land on a single-triplet anchored view (scratch scenario). Universal search returns events + scenarios + Akten with type-scoped result groups. Filter pills (forum/proc/party/kind) reset on mode switch. | — | Event lookup works. |
|
||||
| **B4 — Akte mode + project-backed scenarios** | "Aus Akte" entry mode + page-header Akte picker. Loads project state into the builder (proceeding + perspective + scenario_flags + deadlines actuals). Akte-backed scenarios write through to `paliad.deadlines` + `paliad.projects.scenario_flags`; non-Akte scenarios write to `paliad.scenario_events`. Cross-surface scenario-flag-changed event listener reused from #152 T3. | — | Akte integration works end-to-end. |
|
||||
| **B5 — Share + Promote-to-project wizard** | "Teilen" button + user picker + share row. "Geteilt mit mir" bucket in side panel. "Als Projekt anlegen" opens the 3-step wizard (Bestätigen → Parteien ergänzen → Akte-Metadaten). Successful commit creates project + cascades deadlines + sets `origin_scenario_id`, navigates to /projects/{id}. "Promoted" bucket in side panel. | — | Sharing + promotion work. |
|
||||
| **B6 — Mobile basic-read + cleanup + i18n polish** | Mobile (<640px) shows scenarios + cards read-only; mutating affordances prompt "Auf größerem Bildschirm öffnen". Cleanup: delete dead U0-U4 catalog code (4-tab control, legacy `verfahrensablauf.ts`, etc.). All i18n keys finalised (DE + EN). | — | Mobile works; codebase cleaner. |
|
||||
|
||||
### §7.2 Why this train shape
|
||||
|
||||
- **B0 is DB-only**. The schema can land independently and be exercised via test routes / Supabase MCP before any UI sees it. Keeps mig risk isolated.
|
||||
- **B1-B2 are the MVP**. After B2, a user can build and save a multi-proceeding scenario fully kontextfrei. That alone replaces 60% of today's catalog use.
|
||||
- **B3 adds the lookup path**. After B3, "what's next after Klageerwiderung?" works without saving.
|
||||
- **B4 makes it real**. Akte integration is the load-bearing piece for daily use; ships once the foundation is stable.
|
||||
- **B5 unlocks team value**. Sharing + promotion are the difference between "personal tool" and "team tool". Ship after the core works.
|
||||
- **B6 is cleanup**. Mobile read + dead code removal land last to avoid coupling to in-flight features.
|
||||
|
||||
### §7.3 What stays unchanged
|
||||
|
||||
- URL `/tools/procedures` keeps it (the new builder lives there).
|
||||
- Sidebar entry "Verfahren & Fristen" keeps it.
|
||||
- cmd-K palette keeps it.
|
||||
- `/tools/fristenrechner` + `/tools/verfahrensablauf` legacy redirects (from cronus's U4) stay alive: 301 → `/tools/procedures` (the builder).
|
||||
- `pkg/litigationplanner.CalculateRule` — untouched.
|
||||
- `/admin/procedural-events` — untouched.
|
||||
- `/projects/{id}` Verlauf — untouched (new "Im Builder öffnen" button is the only addition).
|
||||
|
||||
### §7.4 Cleanup at B6
|
||||
|
||||
Dead code to delete (verify with grep before deletion):
|
||||
|
||||
- `frontend/src/components/VerfahrensablaufBody.tsx` (replaced by ProceedingTriplet)
|
||||
- `frontend/src/client/verfahrensablauf.ts` (replaced by builder.ts orchestration)
|
||||
- `frontend/src/client/views/verfahrensablauf-state.ts` (replaced by scenario-backed state)
|
||||
- `frontend/src/client/views/verfahrensablauf-state.test.ts`
|
||||
- `frontend/src/client/verfahrensablauf-detail-mode.ts` (replaced by per-triplet Detailgrad)
|
||||
- Existing scratch tab content in `frontend/src/client/procedures.ts` (4-tab toggling logic, mode routing)
|
||||
|
||||
**Kept**:
|
||||
|
||||
- `frontend/src/client/views/verfahrensablauf-core.ts` (calculation engine; reused by EventCard + ProceedingTriplet)
|
||||
- Legacy URL redirects in Go (`/tools/fristenrechner` + `/tools/verfahrensablauf` → `/tools/procedures`)
|
||||
|
||||
---
|
||||
|
||||
## §8 Open follow-ups (out of scope for v1)
|
||||
|
||||
Tracked for v1.1 / future tickets:
|
||||
|
||||
- **Cross-proceeding peer triggers** (UPC-inf judgment → EPA opp choice deadline). New `paliad.scenario_event_links` table. UI: trigger-picker chip on event cards.
|
||||
- **DE / EPA / DPMA full expansion**. v1 supports EPA + DPMA proceedings at the data layer (calc engine handles them), but the spawn flags and CCR-style nestings are UPC-specific. Other jurisdictions get proper coverage in v1.1.
|
||||
- **Scenario versioning / snapshots**. m's Q8 alternative ("versioned snapshots") deferred. Add when scenarios start driving client briefings.
|
||||
- **Multi-user concurrent editing**. Out of scope. Single-editor model with read-only shares is sufficient until usage shows otherwise.
|
||||
- **Fork-a-shared-scenario**. Read-only sharing in v1 doesn't expose "fork into my workspace". Add when team usage demands it.
|
||||
- **Comments on scenarios / event cards**. Out of scope (separate ticket).
|
||||
- **PDF export of a scenario for client briefings**. Out of scope.
|
||||
- **Mobile-parity edits**. v1.1 — full mobile interaction loop.
|
||||
- **Audit log on scenario edits**. Out of scope (exploratory data).
|
||||
- **Cross-scenario comparison view**. ("Compare planned vs actual" lives on the project page via promote-then-compare; explicit comparison tool is v2.)
|
||||
|
||||
---
|
||||
|
||||
## §9 Synthesis links
|
||||
|
||||
- **mBrian**: file as `[synthesis]` linked `triggered_by` t-paliad-339; `related_to` atlas's reverted tracker design, cronus's unified-procedural-events-tool design, atlas's deadline-system-revision.
|
||||
- **Cross-refs in this repo**: `docs/design-procedures-workflow-tracker-2026-05-27.md` (atlas, reverted), `docs/design-unified-procedural-events-tool-2026-05-27.md` (cronus, live), `docs/design-deadline-system-revision-2026-05-27.md` (atlas Phase 2), `docs/design-fristenrechner-overhaul-2026-05-26.md` (cronus 2026-05-26).
|
||||
- **Gitea**: m/paliad#153 (this PRD), m/paliad#152 (atlas's tracker, reverted), m/paliad#151 (cronus U0-U4 shipped), m/paliad#149 (atlas Phase 2 in flight).
|
||||
- **Coder phase** (deferred per inventor SKILL): runs after m ratifies this PRD. Slice ordering per §7.1. NOT edison (parked at DESIGN READY FOR REVIEW). NOT atlas (just-rejected tracker → framing bias). NOT cronus (parked on Fristenrechner inventor branch). A pattern-fluent Sonnet coder picks up B0 first.
|
||||
|
||||
---
|
||||
|
||||
## §10 Coder hand-off notes
|
||||
|
||||
(Pre-emptive — for whoever picks up B0.)
|
||||
|
||||
- **Migration number**: check `internal/db/migrations/` for the max slot at coder shift start. Two recent migrations (curie's t-paliad-336, ritchie's t-paliad-149 P0) are in flight; coordinate via paliadin/head before claiming a slot.
|
||||
- **Akte integration nuance**: when the builder is in Akte mode and the scenario is project-backed, writes flow to `paliad.deadlines` / `paliad.projects.scenario_flags` instead of `paliad.scenario_*` tables — the scenario row itself just records the canvas view-state (which triplets are visible, ordinal, collapsed state, per-card horizon). This dual-write rule is the load-bearing complexity of B4; design tests for it explicitly.
|
||||
- **Auto-save throttling**: 500ms debounce per change. Avoid PATCH-per-keystroke on notes textareas (use blur-trigger + 2s debounce there).
|
||||
- **Search performance**: universal search (events + scenarios + Akten) needs to stay snappy. Events corpus is ~3000 rows; scenarios/Akten are per-user. Use existing trgm indexes; avoid joining across all three for ranking.
|
||||
- **B5 transactional promotion**: do the wizard's commit in a single Postgres transaction. If any of (project insert / parties / deadlines / scenario status update) fails, roll back atomically. No partial promotions.
|
||||
- **Mobile rendering**: B6 is meant to be cheap. Column-triplet → CSS grid that collapses to single-column at `@media (max-width: 640px)`. Mutating affordances get `pointer-events: none` + a click-handler that surfaces the "Auf größerem Bildschirm öffnen" toast — keeps the desktop interaction code paths unchanged.
|
||||
- **i18n keys**: every user-facing string gets `data-i18n` from B1. Don't accumulate i18n debt across slices.
|
||||
Reference in New Issue
Block a user