Merge: t-paliad-337 — workflow-tracker design for /tools/procedures (m/paliad#152)
Some checks failed
Paliad CI gate / build (push) Has been cancelled
Paliad CI gate / test-go (push) Has been cancelled
Paliad CI gate / deploy (push) Has been cancelled

atlas shipped the workflow-tracker design after m's 21:01 grilling-round reframe (single timeline-with-forks, find=search+pills+result-timelines, aux inline, zoom from within full tree). 510-line doc, 2 rewrite iterations.

7 Qs answered in 2 batches (4+3). 5 on-recommendation, 2 divergent:
- Q3 (divergent): multi-proceeding anchor scope — auto-collapse other proceedings to header-only (new §6.5)
- Q7 (divergent): migration strategy — direct replace at T1, no feature flag (§9)

4-slice + cleanup train. T1 ships minimum-viable tracker visibly at /tools/procedures, replacing the catalog UI knuth shipped today.

Inventor parks. Head dispatches Sonnet coder (NOT atlas per project memory directive).
This commit is contained in:
mAi
2026-05-27 21:14:11 +02:00

View File

@@ -0,0 +1,510 @@
# Design — `/tools/procedures` workflow tracker (m/paliad#152)
**Task:** t-paliad-337
**Gitea:** m/paliad#152
**Inventor:** atlas (shift-1, fresh — name-recycle, not the atlas from earlier today)
**Date:** 2026-05-27
**Branch:** `mai/atlas/inventor-extend-tools`
**Status:** Draft — coder gate held; m to ratify the remaining open questions via `AskUserQuestion` before any coder shift.
**Builds on:**
- `docs/design-unified-procedural-events-tool-2026-05-27.md` (cronus's U0-U4 design, shipped today as `/tools/procedures`)
- `docs/design-deadline-system-revision-2026-05-27.md` §3.3 + §3.3a (atlas Phase 2 model layer + view-mode toggle)
- `docs/design-fristenrechner-overhaul-2026-05-26.md` (cronus 2026-05-26 Mode A+B+result, shipped via t-paliad-322)
**Reframe note (2026-05-27 21:01):** the first draft of this doc overengineered the surface — three-view toggle, separate compound drawer, separate Konstellationen drawer. m re-anchored: "clean display of timelines that have potential forks the user can select. UX should be key. It should be easy to find your thing." This rewrite collapses to a single canonical shape and folds the zoom / constellation / cross-cut concepts into it. The pre-grilling §13 + the 11-Q batch in §14 of the first draft are gone — superseded by m's 4 answers in §0.2 and the smaller open-question set in §10.
---
## §0 Premises
### §0.1 What shipped today and what m hit
`/tools/procedures` (U0-U4, knuth, m/paliad#151) is a **catalog browser**:
- 4 always-visible tabs (Verfahren wählen / Direkt suchen / Geführt / Aus Akte).
- Shared filter strip + search box at the top (markup-only in U0).
- Two output shapes — TREE (Verfahrensablauf) and LINEAR (Mode A/B result view) — bound to specific entry tabs.
m's bugs (2026-05-27 20:43 / 20:46):
1. 4 tabs visible → pre-form leaks across them, page feels like 4 disjoint workflows.
2. Result view fires too many rules incl. conditional-flag-off + curie's 7 compound rules.
3. Proaktiv/Gericht/Reaktiv columns are a stance grouping, not a sequence anchor.
4. No "you are here" marker.
5. Sequence isn't visualised as a sequence — flat priority groups, not chained.
m's reframe (verbatim, 20:43): "view proceedings with all possible constellations and the sequences and determine **where we are** in that sequence and **what steps are coming next** for any given procedural event."
Tightened by m on 21:01:
> "clean display of timelines that have potential forks the user can select. procedural_events that act as triggers for mandatory or optional events. And there is a limited type of proceedings — a sequence of the events builds the proceeding. Some aux proceedings, some main… but a lot is connected. UX should be key. It should be easy to find your thing."
### §0.2 The four m-answers that lock the architecture
Asked back during the grilling round at 20:57, answered 21:01:
| | inventor's grilling question | m's answer | what it locks |
|---|---|---|---|
| 1 | One canonical shape or still 3 views? | "I still want zoomability for one event and all events it triggers. But that can be from within the full timeline/tree as well." | **One canonical view** (full timeline/tree); zoom is an *interaction* on it, not a separate view. The Anchor / Verfahren / Konstellationen toggle is dropped. |
| 2 | What's a "fork" — scenario flags only / +optionals / everything? | "c" (everything: flags + optionals + appeal-target + court-set picks) | **Every choice point in the data is a fork.** Optionals (priority='optional') + conditional flags + appeal-target + perspective + court-set scheduling. Inline pickers. |
| 3 | "Easy to find" — timeline-as-index / search box / proceeding picker first? | "all of these — text search, filter pills, a display of the resulting proceedings timelines" | **Find = combined affordance.** Text search + filter pills + the displayed result *is* the matched proceeding timelines. The page never has chrome that isn't either the find affordance or the timelines themselves. |
| 4 | Aux proceedings inline or drillable? | "inline" | **Aux proceedings draw inline as expandable child timelines** hanging off the spawn point in the parent timeline. The full connected graph is one visible thing. |
### §0.3 Live data the tracker has to work against
Verified 2026-05-27 against `paliad.sequencing_rules` (231 published / 242 total):
- 110 chained (parent_id not null) — most rules in a chain.
- 78 trigger-rooted, 4 spawns (cross-PT), 47 court-set, 18 conditional (6 `with_ccr` / 4 `with_amend` / 4 `with_cci` / 4 compound `with_ccr AND with_amend`).
- Biggest single proceeding: `upc.inf.cfi` (50 rules).
- ~46 proceeding types total (UPC 35 / DE 5 / EPA 3 / DPMA 3).
- `paliad.deadlines` carries both `procedural_event_id` and `sequencing_rule_id` → Akte actuals overlay is a direct join.
### §0.4 Scope
**In:** redesign the `/tools/procedures` surface as a single timeline-tree view with inline forks + a combined find affordance.
**Out:**
- Calculator changes.
- Editorial backfill (curie's t-paliad-333 owns the 7 compound rules + R.109 chain). This design is *independent* of curie's column-shape work; compound rules surface inline via parent_id like any other rule, with whatever annotation curie ships.
- `/admin/procedural-events` write surface.
- `/projects/{id}` Verlauf / SmartTimeline — cross-link only.
- youpc.org cross-repo / Outlook sync / PDF export.
---
## §1 The single canonical shape
One page. One view. Top section = find affordance. Below = matched proceeding timelines, each as an inline-forked tree, vertically stacked.
```
┌────────────────────────────────────────────────────────────────────┐
│ [🔍 Suche: Klageerwiderung_____________________] │
│ Forum: [● UPC] [DE] [EPA] [DPMA] │
│ Verfahren: [● Verletzung] [● Widerklage] [Berufung] [Nichtigkeit] … │
│ Partei: [Klägerseite] [● Beklagtenseite] │
│ Akte: HL-2024-001 ▼ Stichtag: 2026-04-01 │
│ │
│ 2 Verfahren passen — Anker: Klageerwiderung (HL-2024-001) │
└────────────────────────────────────────────────────────────────────┘
┌─ upc.inf.cfi · Verletzungsverfahren UPC ─────────────────────────┐
│ │
│ ● Klageerhebung (R.13) 2026-01-15 · Klg · M │
│ │ │
│ ▼ ● Klageerwiderung (R.23.1) 2026-04-01 · Bekl · M │
│ │ ━━━━ DU BIST HIER ━━━━ │
│ │ Optionen für dieses Ereignis: │
│ │ ☑ Widerklage auf Nichtigkeit │
│ │ ☐ Antrag Patentänderung (R.30) │
│ │ ☐ Vorläufige Einwendungen │
│ │ │
│ ├─● Replik (R.29.a/b) 2026-06-01 · Klg · M │
│ │ ├─● Duplik (R.29.c) 2026-07-01 · Bekl · M │
│ │ └─● Replik auf Defence to CCR (R.29.d) 2026-08-01 · Klg · M │
│ │ └─● Rejoinder (R.29.e) 2026-09-01 · Bekl · M │
│ │ │
│ ├─● Widerklage auf Nichtigkeit ✓ │
│ │ └─▼ Tochterverfahren upc.rev.cfi ▾ │
│ │ │ │
│ │ ├─● Antrag Patentänderung (R.50) optional ☐ │
│ │ ├─● Hauptverhandlung [Gericht] │
│ │ └─● Entscheidung [Gericht] │
│ │ │
│ └─● Vorläufige Einwendungen ☐ (optional, ausgewählt: nein) │
│ │
│ ● Mündliche Verhandlung [Gericht bestimmt] │
│ │ │
│ └─● Urteil [Gericht] │
│ └─▼ Berufungsverfahren upc.apl ▸ (auf Endentscheidung) │
│ │
└────────────────────────────────────────────────────────────────────┘
┌─ upc.ccr.cfi · Widerklage auf Nichtigkeit (Tochter, oben verlinkt) ┐
│ … │
└────────────────────────────────────────────────────────────────────┘
```
No tabs. No view toggle. The output reacts to the find affordance, the anchor pin, and per-node fork selections.
### §1.1 The shape's components
1. **Find header** (sticky at top): search input + filter pills + Akte/date row + a one-line result summary. §2.
2. **Timeline-trees** (the page body): one block per matched proceeding, full chain + inline forks + inline aux branches. §3-§5.
3. **Anchor pin** (when set): the "DU BIST HIER" band on a specific node, optionally with zoom mode collapsing everything else. §6.
That's the entire UI surface. No drawers, no separate drillable panes, no constellation viewer. Forks are inline checkboxes; aux proceedings are inline expandable subtrees; zoom is an interaction on the existing rendering.
---
## §2 The find affordance
m's #3 answer makes this load-bearing: text + pills + result-timelines are all the same affordance. As the user narrows, the timelines below filter; as the timelines change, the result-count summary updates; clicking a node in a timeline auto-narrows the filter pills to that proceeding (optional sugar).
### §2.1 Composition
| Control | Source | Composes via | Persists in |
|---|---|---|---|
| Free-text search | input box, debounced 200ms | OR-against (procedural_event.name DE/EN, rule_code, aliases) | `?q=<text>` |
| Forum pill row | static enum (UPC/DE/EPA/DPMA), single-select | AND | `?forum=<id>` |
| Verfahren pill row | proceeding_type chips, multi-select (deduped from active forum) | AND (any-of) | `?procs=<csv>` |
| Partei pill row | claimant / defendant / both / — (or auto from Akte) | AND | `?party=<x>` |
| Akte picker | dropdown of user's projects | seeds Verfahren + Partei + scenario_flags + anchor | `?project=<uuid>` |
| Stichtag (date) | date input, defaults today | feeds computed dates throughout the timelines | `?trigger_date=<iso>` |
All controls live in one sticky header. The header keeps its height stable so the timelines below don't reflow on every keystroke.
### §2.2 Cold open behaviour
No URL params, no Akte:
- Search box empty, all forums neutral, all proceeding pills neutral. Show a curated default of the most-common proceedings: `upc.inf.cfi`, `upc.rev.cfi`, `upc.apl.merits`, `de.inf.lg`, `epa.opp.opd`, `dpma.opp.dpma`. (See Q4 below.)
- A hint above the timelines: "Suche oder filtere, um andere Verfahren einzublenden."
With a `?project=` param: filters pre-fill from the Akte, anchor pins to the latest completed deadline.
With a `?q=` or `?event=` param: filters pre-fill to match, single matched proceeding renders pinned.
### §2.3 What the search matches
Free-text search hits the same corpus the existing `/api/tools/fristenrechner/search?kind=events` endpoint covers — procedural_events by name + code + aliases. Spawn-only events stay filtered out (per atlas P0 §2.2). Hits surface in two ways simultaneously:
- The matched proceeding(s) render expanded with the hit event(s) anchor-pinned.
- A small "Treffer: 3 Ereignisse in 2 Verfahren" summary above the timelines.
If the user types something narrow enough to match a single event, the page auto-pins that event (auto-anchor). If multiple events match, the user picks via a small dropdown under the search input — picking sets the anchor.
### §2.4 Why pills, not chips-with-sub-modes
The shipped 4-tab UI tried to express "what kind of question are you asking" via tabs. m's answer #3 collapses that — the find affordance doesn't care which "kind" of question; it cares about the active filter set. A user with a search + a forum + an Akte set gets the right timelines regardless of which tab they "came from". The mental model is: narrow the set; the timelines arrive.
---
## §3 Timelines and forks
Each matched proceeding renders as one card. Inside the card: the proceeding's name + jurisdiction badge in a thin header strip, then the chain.
### §3.1 The chain
Vertical, top-to-bottom = chronological. Each node = one procedural_event (the rule that fires it lives inside). Edges = parent_id. Per node:
- **Bullet style** by priority: solid filled (mandatory), solid outline (recommended), dotted (optional), dashed (conditional-flag-off and hidden).
- **Bullet colour**: priority band — black/grey/blue/light depending on the scale we end up picking. Lime accent (`#c6f41c`) reserved for the anchor pin.
- **Inline metadata**: name, rule code, computed date, party badge, priority badge. Stripped to one line.
- **Court-set events**: render with `[Gericht bestimmt]` in date column.
- **Spawn nodes**: terminate the bullet with `▼ Tochterverfahren <code> ▾` — expandable inline; collapsed by default unless the spawn flag is on. §5.
### §3.2 Forks — every choice point is one
A "fork" is anywhere the user can flip the proceeding's shape:
1. **Scenario flags** (`with_ccr`, `with_amend`, `with_cci`) — currently 3, extensible via curie's `scenario_flag_catalog`.
2. **Optional rules** (`priority='optional'`) — each is a "do I do this?" pick.
3. **Appeal-target picks**`applies_to_target` array on appeal proceedings (endentscheidung / kostenentscheidung / anordnung / schadensbemessung / bucheinsicht). Per-card chip group at the appeal root.
4. **Perspective** — claimant / defendant per proceeding (mostly comes from Akte's `our_side`, picker overrides).
5. **Court-set timing choices**`choices_offered` JSON on `sequencing_rules` (`appellant` / `include_ccr` / `skip` shapes from einstein). Per-card chip set.
**Where forks render.** Inline, *on the node where the fork's effect begins.* Not in a top-of-page flag strip (m's bug #5 — sequences should be visualised as sequences; flags above the tree decouple cause from effect).
Concretely: the `with_ccr` fork renders as a checkbox **on the Klageerwiderung node**, because that's where the user decides "we are filing a Widerklage with our KEW". Toggling it lights up the CCR child branches below. Similarly:
- `with_amend` renders on the KEW node *and* on the Antrag-Patentänderung node it gates.
- `with_cci` renders on the Defence-to-Revocation node.
- Optional rules render as a checkbox on their own card.
- Appeal-target picks render on the appeal root.
If multiple forks share a node, they cluster as a small "Optionen für dieses Ereignis" mini-strip *below* the node header:
```
▼ ● Klageerwiderung (R.23.1) 2026-04-01 · Bekl · M
│ Optionen:
│ ☑ Widerklage auf Nichtigkeit
│ ☐ Antrag Patentänderung (R.30)
│ ☐ Vorläufige Einwendungen einlegen
```
### §3.3 Default rendering ("Gewählt" semantics)
Each node renders iff:
- It's mandatory (priority='mandatory'), OR
- It's selected per current scenario state (priority='recommended' unless explicitly deselected; priority='optional' iff explicitly selected; conditional iff flag is on).
- Same as atlas P3's "Gewählt" view-mode.
Conditional rules whose flag is off **do not render at all** by default. The fork checkbox to *turn the flag on* still appears on the gating node — turning it on causes the dependent branch to render.
This is m's bug #2 fix: no more dump of every-rule including flag-off conditional. The forks themselves are the affordance that brings hidden branches into view.
### §3.4 Optional reveal — "Alle Optionen"
A single toggle at the top of each proceeding card (NOT page-wide):
```
[· Gewählt ·] [ Alle Optionen ]
```
"Alle Optionen" renders every rule including flag-off conditionals (greyed with their flag hint) and unselected optionals (dotted with `[Aufnehmen]` chip). Useful when the user wants to see the whole shape. Per-proceeding so a page with 3 proceedings can have one in "Alle Optionen" mode without affecting the others. State persists in `localStorage` per proceeding code.
### §3.5 Why dropping "Nur Pflicht"
Atlas P3's three-way toggle had Nur Pflicht / Gewählt / Alle Optionen. With forks made inline + per-node, "Nur Pflicht" loses load-bearing — it was useful when the page had no fork interactivity (you wanted to dial down clutter). Now the user can just leave all forks off and see the mandatory-only chain in Gewählt mode. The two-way Gewählt ↔ Alle Optionen is enough.
### §3.6 Cross-party rows
Per atlas §2.4 / m's lock: every rule for the proceeding renders, with rows where the user is *not* the primary_party muted + carrying a "Gegenseitig" badge. Same treatment in this tracker. Not hidden by perspective; just visually deemphasised.
---
## §4 Court-set events & date rendering
`is_court_set=true` rules don't compute a date — the court picks it on the day. Two display options that interact:
- Render with `[Gericht bestimmt]` in the date column, no date. Standard.
- When the user has scheduled the actual date (an `appointments` row on the project or a manual override), the actual date replaces the badge. Akte-only.
If the date is "vom Gericht" and matters as a trigger for downstream events, downstream events render `[abhängig von Verhandlung]` instead of a date, and recompute live once the court date is known.
`choices_offered` per-rule (the 3 known shapes today: `appellant`, `include_ccr`, `skip`) — also inline per-node, treated as forks (§3.2 #5).
---
## §5 Aux proceedings inline
Per m's #4 answer: spawned proceedings draw inline as expandable subtrees, not as drillable separate pages.
### §5.1 Render
A spawn node renders as a leaf chip terminating the parent's chain segment:
```
●─● Widerklage auf Nichtigkeit ✓
└─▼ Tochterverfahren upc.rev.cfi ▾
├─● Antrag Patentänderung (R.50) optional ☐
├─● Hauptverhandlung [Gericht]
└─● Entscheidung [Gericht]
└─▼ Berufungsverfahren upc.apl ▸
```
- Collapsed by default unless the gating fork is on (e.g. `with_ccr` ticked → CCR's spawn into upc.rev.cfi auto-expands).
- Expanding writes nothing — pure UI state in `sessionStorage["procedures:expanded_spawns"]`.
- The aux subtree renders with the same node vocabulary as the parent; forks inside the aux are independently editable.
- Aux subtrees can themselves have aux subtrees (e.g. CCR → Berufung). Depth is bounded by the data — today 2 levels deep at most.
### §5.2 Cross-references
When two paths converge on the same aux proceeding (e.g. CCR triggers from a couple of places), the aux renders inline at the first path's spawn point and renders as a back-reference at subsequent spawn points: `▸ (siehe oben: Tochterverfahren upc.rev.cfi)`. Single source of truth in the rendered tree, even when the graph has multiple edges.
### §5.3 Akte mode
In Akte mode, if the spawn was actualised (a child project exists linked via `parent_project_id`), the aux subtree shows the child project's badge: `📁 HL-2024-001-CCR · Tochterakte`. Clicking the badge navigates to that child project. The subtree itself still renders inline.
---
## §6 Anchor pin & zoom
m's #1 answer: "zoomability for one event and all events it triggers, from within the full timeline".
### §6.1 The anchor pin
Any node can be pinned as the anchor. Pinning sources:
- URL `?event=<sequencing_rule_id>` (deep link).
- Search box auto-pin when the search narrows to a single hit.
- Click-to-pin on any node (small pin icon in the node's metadata row).
- Akte landing: auto-pin to latest completed deadline.
The pinned node renders with a 4px lime-coloured left band + a `━━ DU BIST HIER ━━` divider above its successors. The pin is also reflected in the find-header's result summary: "Anker: Klageerwiderung (HL-2024-001)".
### §6.2 Zoom mode
A small `[ 🔍 Fokus ]` chip on the anchored node toggles zoom mode for that anchor. When zoom is on:
- The anchored node's ancestors collapse to a single breadcrumb at the top of the proceeding card:
```
upc.inf.cfi ▸ Klageerhebung ▸ ━ Klageerwiderung ━
```
- The anchored node renders full.
- Successors render fully (the forward subtree under the anchor).
- Sibling branches at every ancestor depth collapse to a single-line summary card: `… 4 weitere Schritte verborgen — [zeigen]`.
This is what m means by "zoom into one event from within the timeline" — the *same view*, just with non-relevant siblings collapsed. Toggle off → full timeline restored, anchor still pinned.
Zoom is page-scoped (one anchor per page, one zoom state). State in URL: `?event=<id>&zoom=1`.
### §6.5 Multi-proceeding anchor scope (m's Q3 divergence)
When the page shows >1 matched proceeding *and* an anchor is pinned, the non-anchored proceedings auto-collapse to a header-only one-line card:
```
┌─ upc.inf.cfi · Verletzungsverfahren UPC ─────┐
│ … full timeline … │
│ ━━ DU BIST HIER: Klageerwiderung ━━ │
└──────────────────────────────────────────────┘
┌─ upc.rev.cfi ▸ ausblenden — [zeigen] ────────┐
└──────────────────────────────────────────────┘
┌─ upc.apl.merits ▸ — [zeigen] ────────────────┐
└──────────────────────────────────────────────┘
```
Clicking a header card's `[zeigen]` link restores that proceeding's full timeline (the header stays as a per-card affordance for re-collapse). The collapsed state persists in `sessionStorage["procedures:collapsed_proceedings"]`. Un-pinning the anchor restores all visible proceedings to full-render automatically.
The rule applies regardless of how the anchor was pinned (URL, search-auto, click-to-pin, Akte landing). The find-header result count still shows N proceedings matched — header cards are present, just collapsed.
### §6.3 The "where I came from" question
m's brief asked for backward-walk visualisation. Without zoom: the chain above the anchor is the backward walk — it's the same tree. With zoom: the breadcrumb at the top of the proceeding card is the backward walk in compact form. No separate concept; backward walk = upward in the tree.
### §6.4 Akte mode: actuals overlay
When `?project=<uuid>` is set, each node in the chain queries `paliad.deadlines WHERE project_id = $p AND sequencing_rule_id = $r` and overlays:
- `status='done'` → ✓ in the node bullet area + actual completed date in the date column. Greyed slightly to read as "past".
- `status='open'` and `due_date < today` → ⚠ overdue.
- `status='open'` and `due_date >= today` → 📅 actual due date if differs from computed; ◇ marker.
- No deadline row → render as template (current behaviour).
The anchor auto-pins to the latest `status='done'` deadline by default — the natural reading is "we just finished this".
---
## §7 What lives where: the find header vs the timelines
A short table to make the responsibility boundary explicit:
| Concern | Find header | Timeline body |
|---|---|---|
| Pick proceeding(s) | Filter pill row | (auto-rendered after) |
| Pick anchor | Search-narrow → auto-pin / URL `?event=` | Click pin icon on any node |
| Pick perspective | Pill (or auto from Akte) | (read-only — feeds rendering) |
| Pick scenario flags | (no) | Inline fork checkboxes on gating nodes |
| Pick optional rules | (no) | Inline fork checkboxes on each optional node |
| Pick appeal target | (no) | Inline chip group on appeal root |
| Pick date | Stichtag input | (read-only — feeds computed dates) |
| Toggle Alle Optionen / Gewählt | (no) | Per-proceeding 2-way toggle |
| Zoom on anchor | (no) | `[Fokus]` chip on anchored node |
| Akte select | Akte picker | (read-only — feeds actuals overlay) |
Find header = "narrow the set + global context". Timelines = "everything per-event". No drawers, no overlays.
---
## §8 Cold open + empty state
Cold open with no Akte, no URL params (Q4 below): show a curated default of 6 most-common proceedings (`upc.inf.cfi`, `upc.rev.cfi`, `upc.apl.merits`, `de.inf.lg`, `epa.opp.opd`, `dpma.opp.dpma`), each rendered with default Gewählt + no forks selected + no anchor. Hint text above: "Suche oder filtere, um andere Verfahren zu sehen."
Empty filter result (e.g. user types nonsense): zero timelines render, with a helper card: "Keine Treffer. Filter zurücksetzen ▸"
---
## §9 Migration (direct replace per m's Q7)
4 slices + 1 cleanup, all surface, no DB mig, no `?tracker=1` flag. Each slice ships visibly to users at `/tools/procedures`. T1 must be at least as functional as today's catalog browser — so the find header + multi-proceeding render + inline forks + aux inline all front-load there. T2-T4 layer the remaining behaviour.
All independent of curie's editorial work — compound rules render inline via parent_id like any other rule; if curie ships a `compound_predecessors uuid[]` column later, those rules can render at multiple positions (one inline per predecessor) without tracker code changes beyond the join.
| Slice | What ships | Notes |
|---|---|---|
| **T1 — Tracker shell replaces the catalog page** | `/tools/procedures` now renders: sticky find header (search + Forum/Verfahren/Partei pills + Akte picker + global Stichtag), N-proceeding render (one card per matched proceeding), inline forks (scenario flags + optionals visible as checkboxes on the gating node), aux proceedings inline-expandable at spawn points, cold-open with 6 curated defaults (Q4), default = Gewählt. The 4 entry-mode tabs are deleted in the same PR; URL params `?mode=proceeding\|search\|wizard\|akte` 301-redirect or drop. URL anchor `?event=<rule_id>` scroll-highlights the matching node (no zoom yet). | Replaces catalog UI; users see the new tracker immediately. |
| **T2 — Anchor pin + zoom + multi-proceeding scope** | Anchor pin (lime band + DU BIST HIER divider), `[Fokus]` chip on anchored node toggles zoom (§6.2), URL state `?event=…&zoom=1`. Multi-proceeding auto-collapse rule (§6.5) kicks in when an anchor is set. Click-to-pin on any node. | Layered on T1's existing render. |
| **T3 — Akte landing + actuals overlay** | `?project=<uuid>` derives anchor from latest `status='done'` deadline (Q5), backward walk overlays `paliad.deadlines` actuals as status badges (§6.4), scenario_flags load from project, fork write-back via existing `PATCH /api/projects/{id}/scenario-flags` + `POST /api/projects/{id}/deadlines/bulk`. | The first slice that exercises the project hookup end-to-end. |
| **T4 — Appeal-target + court-set choices + polish** | Wire `applies_to_target` array forks on appeal proceedings, `choices_offered` shapes (`appellant`, `include_ccr`, `skip`), court-set date override from `appointments` table, cross-party muted treatment per §3.6. Per-proceeding "Alle Optionen" toggle (§3.4). | Polish + the edge-case fork shapes. |
| **T5 — Cleanup** | Dead-code removal: legacy `procedures.ts` tab toggling, `fristenrechner-mode-a.ts` / `fristenrechner-wizard.ts` / `fristenrechner-result.ts` / `verfahrensablauf.ts` if no longer referenced (verify with grep before deletion). Sidebar/cmd-K unchanged (URL same). | No user-visible change. |
### §9.1 Constraint: T1 is the new floor
Because there's no flag, **T1 must not regress** from today's catalog UI in any non-trivial way. The catalog today serves four user workflows:
1. **Pick a proceeding, see its full Verfahrensablauf** → T1 covers via "Verfahren" pill click → that proceeding renders alone.
2. **Search for an event** → T1 covers via search input + auto-pin.
3. **Wizard from R1-R5** → T1 covers via Forum/Verfahren/Partei pills + search (the wizard's narrowing is just a sequence of filter applications).
4. **Enter via Akte** → T1 covers via the Akte picker; full actuals overlay arrives in T3 (open/done badges may render partial in T1, but the Akte's scenario_flags + proceeding pre-load works).
If T1 reviewing exposes a regression, T1 holds (the issue blocks merge) — m's PR review gates the slice landing.
### §9.2 What stays unchanged
- URL: `/tools/procedures` keeps it.
- Sidebar entry "Verfahren & Fristen" keeps it.
- cmd-K palette keeps it.
- All other tools, calendar, projects, deadlines surfaces — untouched.
- Calculator (`pkg/litigationplanner.CalculateRule`) — untouched.
### §9.3 Out-of-band dependencies
- The compound-predecessors editorial column is owned by curie's t-paliad-333. Tracker reads whatever lands. If it slips past T4, compound rules render via their primary parent_id only (today's shape) — degraded but still correct on that path. No tracker re-render needed when curie ships.
- The Akte actuals overlay (T3) reads `paliad.deadlines.sequencing_rule_id` — column exists, nothing new.
### §9.4 Test surface per slice
- **T1**: cold-open 6 curated defaults render; search narrows to single proceeding; pill toggles change render; `?project=` loads Akte filters (no actuals yet); URL deep-link `?event=` highlights matching node; legacy `?mode=` redirects.
- **T2**: click-to-pin sets anchor with lime band; `[Fokus]` zoom collapses siblings; un-zoom restores; multi-proceeding auto-collapse when anchor active; URL state survives reload.
- **T3**: Akte landing auto-pins latest done deadline; status badges render on each node from `paliad.deadlines`; fork tick writes to `scenario_flags`; "In Akte speichern" persists.
- **T4**: appeal-target chips switch the rule set rendered on appeal proceedings; `choices_offered` per-node chip groups visible + functional; "Alle Optionen" reveals hidden conditional rules with greyed state.
- **T5**: production deploy unchanged surface; no live regression; deleted files don't break build.
---
## §10 Open questions for m
Seven questions in 2 batches (4 + 3) for `AskUserQuestion`. Tier 1 = how the per-node fork UI feels + how zoom interacts with multi-proceeding pages. Tier 2 = cold-open content + Akte default + Stichtag scope + migration cadence.
m's picks fold back into §11 below before the "TRACKER DESIGN READY FOR REVIEW" signal.
### Batch 1 — fork UI + zoom + cross-party
- **Q1 (Fork-cluster shape on a node)** — when a node has 2-4 forks (e.g. Klageerwiderung: `with_ccr` + `with_amend` + Vorl. Einwend.) — (a) inline checkbox list below the node header (current sketch), (b) collapsed "Optionen (3) ▾" affordance that expands on hover/click, (c) chip strip on the same line as the node header.
- **Q2 (Zoom interaction)** — `[Fokus]` chip on the anchored node — (a) collapses siblings to one-line summaries (current sketch), (b) outright hides siblings, breadcrumb-only, (c) split-view (zoomed pane below original full tree).
- **Q3 (Anchor scope on a multi-proceeding page)** — when 3 timelines are visible and the user pins an anchor in one — (a) the other 2 timelines stay expanded normally (no zoom effect on them), (b) the other 2 timelines auto-collapse to header-only ("upc.rev.cfi ▸ ausblenden — [zeigen]"), (c) the other 2 timelines reorder to bottom of page (anchored proceeding floats to top).
- **Q4 (Cold-open default content)** — opening `/tools/procedures` with no URL params and no Akte — (a) the 6-curated-default-proceedings sketch (Verletzung UPC + DE LG, Nichtigkeit UPC, Berufung UPC, EPA-Einspruch, DPMA-Einspruch), (b) all ~46 proceedings rendered with all forks off (lots of scrolling), (c) empty state with a "Filter wählen, um Verfahren einzublenden" prompt.
### Batch 2 — Akte semantics + Stichtag + migration
- **Q5 (Akte landing — default anchor)** — `?project=<uuid>` — (a) auto-pin to latest `status='done'` deadline (current sketch), (b) auto-pin to next-open deadline (forward-looking), (c) no auto-pin, just pre-fill filters + actuals overlay, user picks anchor.
- **Q6 (Stichtag scope)** — date input in the find header — (a) global, feeds all visible proceedings' computed dates (current sketch — useful for browsing "if today were the trigger"), (b) per-proceeding (each timeline carries its own date input), (c) only valid in single-proceeding mode (hidden when the page shows >1 proceeding).
- **Q7 (Migration cadence)** — (a) flag-gated dev under `?tracker=1`, T1-T4 ship, T5 hard-cut (current sketch, cronus precedent), (b) direct replace at T1 (no flag — every slice ships visibly to users), (c) parallel URL `/tools/procedures-v2` until hard-cut.
---
## §11 m's decisions (2026-05-27)
All 7 questions answered via `AskUserQuestion` in 2 batches (4 + 3) at 21:0?. 5 picks on-recommendation, 2 diverged. Decisions below; the underlying question list lives in §10 above as the historical record.
### Tier 1 — fork UI + zoom + cross-party
- **Q1 (Fork cluster on a node): Inline checkbox list below node header.** [= REC] **Locks §3.2.** Every fork on a given node renders as a checkbox in an "Optionen:" cluster line below the node header. Always visible, no hover, no extra click. Vertical real estate per node is acceptable because the default `Gewählt` mode keeps the tree compact (most events have zero forks).
- **Q2 (Zoom interaction): Collapse siblings to one-line summaries.** [= REC] **Locks §6.2.** `[Fokus]` chip on the anchored node folds sibling branches at each ancestor depth to a `… 4 weitere Schritte verborgen — [zeigen]` line. The anchored node's subtree renders full. Breadcrumb at the top of the proceeding card. Toggle off restores everything.
- **Q3 (Multi-proceeding anchor scope): Other timelines auto-collapse to header-only.** [≠ REC; m diverged from "stay expanded"] **Locks new §6.5.** When an anchor is pinned on a multi-proceeding page, the non-anchored proceedings fold to a one-line header card (`upc.rev.cfi ▸ ausblenden — [zeigen]`). Clicking the header line restores that proceeding's full timeline. Rationale (interpreted): with an anchor pinned, the page is *about* that anchor — having other proceedings full-render in parallel competes for attention without earning it. The header card preserves the find-header result count and offers a one-click escape if the user wants to compare.
- **Q4 (Cold open content): 6 curated default proceedings.** [= REC] **Locks §8.** No URL params + no Akte → render `upc.inf.cfi`, `upc.rev.cfi`, `upc.apl.merits`, `de.inf.lg`, `epa.opp.opd`, `dpma.opp.dpma` stacked vertically, all forks off, no anchor. Hint: "Suche oder filtere, um andere Verfahren zu sehen."
### Tier 2 — Akte + Stichtag + migration
- **Q5 (Akte default anchor): Latest `status='done'` deadline.** [= REC] **Locks §6.4 + §9.** `?project=<uuid>` → derive anchor by `SELECT … FROM paliad.deadlines WHERE project_id=$p AND sequencing_rule_id IS NOT NULL ORDER BY completed_at DESC NULLS LAST LIMIT 1`. Fallback: next open deadline → proceeding root. The backward chain reads as Akte history; the anchor itself is the most recently completed work; forward is upcoming.
- **Q6 (Stichtag scope): Global, feeds all visible proceedings.** [= REC] **Locks §2.1 + §7.** One date input in the find header. All visible proceedings compute dates against it. When the user has an Akte loaded, the Stichtag pre-fills from the project's latest trigger date but is overrideable. When the anchor is pinned to a `status='done'` deadline, the date input shows that deadline's `completed_at` but can still be overridden for "what-if" exploration.
- **Q7 (Migration cadence): Direct replace at T1 — no flag.** [≠ REC; m diverged from flag-gated dev] **Rewrites §9.** Every slice ships visibly to users at /tools/procedures. T1 must be at minimum equivalent to today's catalog browser (so the slicing has to front-load find header + multi-proceeding render + forks inline + aux inline). The flag-gated dev plan is dropped. cronus's Q11 hard-cut precedent extends here: m would rather ship per-slice visibly than carry a parallel surface during dev. Rationale (interpreted): partial-tracker > no-tracker, and ~50 internal lawyers absorb the per-slice deltas through release comms.
### §11.1 Changes triggered by m's divergences
**Q3 divergence — multi-proceeding anchor scope.** New §6.5 added below. The header-card-only render for non-anchored proceedings preserves filter compose (you can still see "upc.rev.cfi matched the filter") while clearing the page's vertical real estate for the anchor's full context.
**Q7 divergence — direct replace.** §9 rewritten end-to-end. T1 now ships the minimum-viable tracker (find header + multi-proceeding render + forks inline + aux subtrees inline + URL-anchor highlight), replacing the catalog UI at /tools/procedures from the moment it merges. T2-T4 layer zoom, Akte semantics, polish. T5 ("cleanup only") is now just dead-code removal.
### §11.2 What stays unchanged
The other 5 picks (Q1, Q2, Q4, Q5, Q6) ratified the inventor proposal. Inline checkbox forks per node, breadcrumb-collapse zoom, 6-curated cold open, latest-done-deadline Akte anchor, global Stichtag — all locked as drafted in §1-§8.
---
## §12 Out of scope
- Calculator changes.
- Editorial backfill (curie's t-paliad-333). Compound rules render inline as parent_id-chained rules with curie's annotation; no special tracker treatment.
- `/admin/procedural-events`, `/projects/{id}` Verlauf / SmartTimeline.
- youpc.org / Outlook / PDF export.
- Multi-project anchor comparison.
- Free-text scenario flag i18n.
---
## §13 Synthesis links
- **mBrian** (after m's ratification): file as `[synthesis]` linked `triggered_by` t-paliad-337; `related_to` cronus's unified-procedural-events-tool design + atlas's deadline-system-revision + cronus's earlier Fristenrechner overhaul.
- **Cross-refs in this repo**: `docs/design-unified-procedural-events-tool-2026-05-27.md` (cronus, U0-U4 shipped today), `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#152 (this design), m/paliad#151 (cronus U0-U4), m/paliad#149 (atlas Phase 2).
- **Coder phase** (deferred per inventor SKILL): runs after m ratifies §10 + §11. Slice ordering per §9. NOT atlas (parked at "TRACKER DESIGN READY FOR REVIEW"). Pattern-fluent Sonnet coder picks up T1 first.