# 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.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='pending', 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`~~ — KEEP. Builder imports `filterByDetailMode` from it; per-triplet Detailgrad reuses this module.
- 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.
