diff --git a/docs/plans/prd-procedures-litigation-planner-2026-05-27.md b/docs/plans/prd-procedures-litigation-planner-2026-05-27.md new file mode 100644 index 0000000..4f90d23 --- /dev/null +++ b/docs/plans/prd-procedures-litigation-planner-2026-05-27.md @@ -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 row's `status` flips to `promoted`, `promoted_project_id` points back. Builder navigates to `/projects/`. +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=` + 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=` + 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 ]` until the court date is filed, then auto-recompute. + +### §3.6 Spawn (child) proceedings + +When a triplet has a `with_` 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 · schreibgeschützt" at the top of the canvas. No "Fork to my workspace" affordance in v1. +- **URL contract**: minimal, view-state only — `?scenario=&mode=&event=` (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 = + 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= + 5. Navigate to /projects/ +``` + +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.