From 6e585951ee23b747272f5f71fc6ca5cdbf0269d9 Mon Sep 17 00:00:00 2001 From: mAi Date: Tue, 26 May 2026 10:21:23 +0200 Subject: [PATCH] =?UTF-8?q?docs(litigation-planner):=20fold=20m's=20AskUse?= =?UTF-8?q?rQuestion=20picks=20=E2=80=94=20new=20paliad.scenarios=20table?= =?UTF-8?q?=20+=20jsonb=20spec,=20no=20user-authored=20rules=20(t-paliad-2?= =?UTF-8?q?92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit m's 2026-05-26 decisions: - Q1 composition: primary+spawned (v1) with multi-proceeding peer compose as v2 goal — jsonb spec architected for N entries from day 1 - Q2 scope: per-project + abstract (project_id NULL = abstract saved templates) - Q3 dates: per-anchor overrides over one base date (matches today's compute) - Q4 storage: new paliad.scenarios table with jsonb spec (NOT project_event_choices column extension) - "users should not add their own rules" — original Slice E (user-authored rules) DROPPED, replaced with abstract scenarios surface on /tools/verfahrensablauf §5 rewritten with new schema (paliad.scenarios + active_scenario_id FK), jsonb spec shape (proceedings[] array, version-tagged), validate-on-load discipline, multi-peer v2 path. §6 struck-through with original body preserved as historical context. §10 slice plan revised: Slice E = abstract scenarios surface, not user-authored rules. §0.5 added with decision matrix; §13 marked resolved. Package shape (§2 §3) unchanged — library was decoupled from persistence/UI choices by design. --- docs/design-litigation-planner-2026-05-26.md | 294 +++++++++++++------ 1 file changed, 202 insertions(+), 92 deletions(-) diff --git a/docs/design-litigation-planner-2026-05-26.md b/docs/design-litigation-planner-2026-05-26.md index 9c61205..dc43a13 100644 --- a/docs/design-litigation-planner-2026-05-26.md +++ b/docs/design-litigation-planner-2026-05-26.md @@ -8,6 +8,27 @@ --- +## §0.5 m's decisions (2026-05-26, AskUserQuestion round) + +After Q1+Q2 escalation, m chose AskUserQuestion-mode + revised Q2 framing. Four-question round answered live: + +| Q | Topic | m's pick | Note | +|---|---|---|---| +| §0.5-Q1 | Composition shape | **Primary + spawned (v1)** with **multi-proceeding peer compose as the goal (v2)** | "Usually it would be 1 — but 2 is the goal. So people can create more complex scenarios." The jsonb spec is architected for N entries from day 1; v1 ships with N=1; v2 adds peers without a schema migration. | +| §0.5-Q2 | Scope | **Per-project + abstract** | Scenarios attach to a real Akte (project_id non-NULL) AND can be created standalone on /tools/verfahrensablauf (project_id NULL = abstract saved templates). | +| §0.5-Q3 | Trigger dates | **Per-anchor overrides over one base date** | Matches today's compute model — one trigger date + AnchorOverrides for known court-set / extended dates. Per-proceeding dates inside the spawn graph derive from base + parent chain. | +| §0.5-Q4 | Storage | **New `paliad.scenarios` table with jsonb spec** | Beats the column-extension option because the spec carries multi-proceeding compose, abstract-vs-project distinction, and absorbs future schema churn inside jsonb. | + +**Additional clarification (m's text):** *"users should not add their own rules. I want them to setup a scenario with different existing proceeding types / submissions that trigger sequences."* + +→ **Original Slice E (user-authored rules) is DROPPED.** `paliad.deadline_rules.project_id` column is OUT. Scenarios are pure compositions of existing rules/proceedings/submissions — never authored corpus. §6 below is preserved as historical context; the slice plan in §10 is revised. + +→ **New Slice E** = abstract scenarios surface on `/tools/verfahrensablauf` (Speichern affordance + per-user saved-template list). Different chrome, same scenarios table, same engine. + +§5 has been rewritten to match these picks; §6 is preserved struck-through; §10 slice plan is revised. The package layout in §2 and the public API in §3 are unchanged by these decisions — the library shape was decoupled by design from persistence + UI choices. + +--- + ## §0 TL;DR > **paliad + youpc.org both import a single Go package — `pkg/litigationplanner`** — that owns the deadline-rule model, the calendar arithmetic, the condition-expression gate, the sub-track routing, and the timeline composer. Persistence stays at the call-site: paliad's `internal/services/*` implements the `Catalog` / `HolidayCalendar` / `CourtRegistry` interfaces against Postgres; youpc.org imports `embedded/upc.Catalog()` + friends and runs the same engine against a generator-produced JSON snapshot of paliad's UPC subset. @@ -20,18 +41,20 @@ The convergence: 4. youpc.org's UPC-restricted reuse rides on an embedded JSON snapshot (UPC subset + UPC/EU holidays + UPC courts) shipped inside the package — `import "mgit.msbls.de/m/paliad/pkg/litigationplanner/embedded/upc"` is the entire integration surface on the youpc side. 5. Scenarios + user-authored rules are paliad-side persistence concerns that the package's request shape (`CalcRequest`) already supports via `RuleOverrides` + per-card `Choices`. The pkg need not grow new contracts for either; only paliad's catalog impl + paliad-side tables grow. -Six slices, each independently shippable on the paliad side; one paired PR on the youpc side at the end: +Six slices, each independently shippable on the paliad side; one paired PR on the youpc side at the end (revised per §0.5): - **Slice A** — atomic extract of calc + types + condition_expr + sub-track + legal-source helpers into `pkg/litigationplanner`. No behaviour change. paliad's `internal/services/fristenrechner.go` becomes a 60-line shell. - **Slice B** — `Catalog` / `HolidayCalendar` / `CourtRegistry` interfaces + paliad's default loaders implementing them. - **Slice C** — embedded UPC snapshot + `scripts/snapshot-upc/main.go` generator. -- **Slice D** — scenarios persistence (`paliad.project_event_choices.scenario_name` + `paliad.projects.active_scenario`). -- **Slice E** — user-authored rules (`paliad.deadline_rules.project_id` nullable column + catalog merge). +- **Slice D** — `paliad.scenarios` table + jsonb spec + per-project chip group. Composition-of-existing-rules (NOT user-authored rules — §6 retracted). +- **Slice E** — abstract scenarios on `/tools/verfahrensablauf` (Speichern als Vorlage + Meine Vorlagen list). Same table, `project_id IS NULL`. - **Slice F** — youpc.org integration (separate PR on the youpc repo). -m's two locked decisions: +m's locked decisions (latest = §0.5): - **Package within paliad** (option 1, not a separate repo). ✓ -- **Inventor must escalate Q1+Q2 to head via `mai instruct`, not AskUserQuestion.** ✓ +- **AskUserQuestion permitted this session** — m flipped the protocol mid-session. ✓ +- **Scenarios = jsonb spec, per-project + abstract, primary+spawned for v1, multi-peer for v2.** ✓ +- **No user-authored rules. Composition only.** ✓ --- @@ -443,86 +466,175 @@ The snapshot stores rows in close to their DB shape (snake_case JSON tags, same --- -## §5 Scenarios design (Q1) +## §5 Scenarios design (m's picks — REVISED 2026-05-26) -### m's framing +### m's framing (verbatim) -> Where we can add proceeding types or specific submissions and then get the whole sequence with options for the steps so we can create scenarios etc as well. +> Where we can add proceeding types or specific submissions and then get the whole sequence with options for the steps so we can create scenarios etc as well. […] users should not add their own rules. I want them to setup a scenario with different existing proceeding types / submissions that trigger sequences. -"Scenarios" = named "what if" projections for a project. The user has a UPC INF case; they want to compare: -- "what if we accept the offer to amend" (with_amend flag) -- "what if we file a CCR" (with_ccr flag) -- "what if both" (with_amend + with_ccr) -- "court-extended replik by 1 month" (anchor override on inf.reply) +A scenario is a **named composition** built from existing proceedings, existing submissions, existing flags, and anchor dates. It is not authored corpus — every rule it references is already in `paliad.deadline_rules`. The scenario glues them together with the user's choices + dates and produces a complete timeline. -Each of these is a complete set of choices. Today they're URL state (`?with_ccr&with_amend&choices=...`). Scenarios make them named + persisted + switchable from the project page. - -### Recommended (R) — Option A from the brief, refined - -Extend `paliad.project_event_choices` with a `scenario_name` column; add `paliad.projects.active_scenario` to track the user's current pick. No new table. +### Recommended design — `paliad.scenarios` table with jsonb spec #### Schema ```sql -- Slice D migration (paliad-side, NOT in pkg/litigationplanner). -ALTER TABLE paliad.project_event_choices - ADD COLUMN scenario_name text NOT NULL DEFAULT 'default'; +CREATE TABLE paliad.scenarios ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + project_id uuid NULL REFERENCES paliad.projects(id) ON DELETE CASCADE, + -- project_id NULL = abstract scenario (saved Verfahrensablauf template). + -- project_id NOT NULL = attached to an Akte (the common case). + name text NOT NULL, + description text NULL, + spec jsonb NOT NULL, + -- spec carries the full composition; see §5.1. + created_by uuid NULL REFERENCES paliad.users(id) ON DELETE SET NULL, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + -- Within a single project, names are unique. Abstract scenarios are + -- unique per (created_by, name) so two users can each have a "with_ccr" + -- saved template without colliding. + CONSTRAINT scenarios_unique_per_scope UNIQUE NULLS NOT DISTINCT + (project_id, created_by, name) +); --- Composite PK now includes scenario_name so a project can have N parallel --- scenarios each with their own choice set. -ALTER TABLE paliad.project_event_choices - DROP CONSTRAINT IF EXISTS project_event_choices_pkey; -ALTER TABLE paliad.project_event_choices - ADD PRIMARY KEY (project_id, scenario_name, submission_code, choice_kind); +CREATE INDEX scenarios_project_id_idx ON paliad.scenarios(project_id); +CREATE INDEX scenarios_abstract_user_idx + ON paliad.scenarios(created_by) WHERE project_id IS NULL; +-- Active scenario per project. NULL = use today's ad-hoc choice state +-- (paliad.project_event_choices) — pre-scenario behaviour preserved. ALTER TABLE paliad.projects - ADD COLUMN active_scenario text NOT NULL DEFAULT 'default'; + ADD COLUMN active_scenario_id uuid NULL + REFERENCES paliad.scenarios(id) ON DELETE SET NULL; -CREATE INDEX project_event_choices_scenario_idx - ON paliad.project_event_choices(project_id, scenario_name); +-- RLS: project-scoped scenarios inherit team visibility via +-- paliad.can_see_project(project_id). Abstract scenarios are +-- created_by-owner-only (private) for v1; later we can add a shared flag. ``` -The default scenario (`'default'`) is automatic — every existing row backfills there, every new project starts there. The user is never *required* to name a scenario. +`paliad.project_event_choices` is **NOT modified** by this design. It continues to store the un-named "current" choice state for back-compat; once a project switches to a saved scenario, the engine reads from `scenarios.spec` instead. + +#### Spec shape (jsonb) + +Architected for multi-proceeding compose from day 1 — v1 ships with N=1 entries under `proceedings[]`; v2 lifts to N=peer. + +```json +{ + "version": 1, + "base_trigger_date": "2026-05-26", + "proceedings": [ + { + "code": "upc.inf.cfi", + "role": "primary", + "flags": ["with_ccr", "with_amend"], + "per_card_choices": { + "inf.r30_amend": {"appellant": "claimant"}, + "inf.rejoin": {"include_ccr": true} + }, + "anchor_overrides": { + "inf.reply": "2026-08-15", + "inf.urteil": "2027-02-01" + }, + "skip_rules": ["inf.r30_amend_response"] + } + // v2 (future): additional peer proceeding entries here, each with + // its own trigger_date override + flags + choices + anchors. + ] +} +``` + +Field semantics: + +- `version` — schema version of the spec format. v1. Bumped when the spec shape changes incompatibly. +- `base_trigger_date` — the case's anchor date (e.g. when the Klage was filed). Every proceeding without its own `trigger_date_override` uses this. +- `proceedings[]` — array of proceeding entries: + - `code` — proceeding type code (e.g. `upc.inf.cfi`). + - `role` — `"primary"` (the main proceeding) or `"peer"` (v2 extension; ignored in v1). + - `trigger_date_override` — optional ISO date, defaults to `base_trigger_date`. v1 uses the base; v2 enables per-proceeding offsets. + - `flags[]` — array of flag strings (`with_ccr`, `with_amend`, `with_cci`). + - `per_card_choices` — map of `submission_code → {appellant?, include_ccr?, …}` (today's PerCardAppellant + IncludeCCRFor merged into one shape). + - `anchor_overrides` — map of `submission_code → ISO date` (today's AnchorOverrides — court-extended deadlines, court-set decision dates known retroactively). + - `skip_rules[]` — array of submission_codes the user has opted out of (today's SkipRules). + +Cross-proceeding spawns (the existing `is_spawn` graph — AMD / APP / CCR spawned from primary INF) are handled **automatically by the engine inside the primary entry** — the user does not list spawn outcomes in the spec; the engine expands them via `expandCrossProceedingSpawns`. The spec only carries primary picks + (later) peer picks. + +#### Why jsonb spec + +- **Multi-proceeding-ready from day 1.** v1 has 1 entry in `proceedings[]`; v2 has N. No schema migration needed for the lift. +- **Absorbs future per-card-choice keys** without ALTER TABLE. The choices vocabulary (`appellant`, `include_ccr`, …) is already extension-friendly inside the calculator; the spec inherits that. +- **Single round-trip** to load a scenario — no joins. +- **Validatable at write time** — a JSON-schema check in the service layer asserts every `code` resolves to an active proceeding, every `submission_code` resolves to a rule, every flag is known. Bad scenarios cannot be saved; the user gets a clear error. + +#### Engine integration + +paliad's `FristenrechnerService.Calculate` is unchanged. The new code path is the **request builder**: when a project has `active_scenario_id` non-NULL, paliad reads the scenario's spec, builds `CalcOptions` from the primary proceeding entry, and calls `Calculate(ctx, primary.code, scenario.base_trigger_date, opts)`. The package gets the same `CalcOptions` shape it's always gotten. + +v2 multi-peer expansion: iterate `proceedings[]`, call `Calculate` per entry, merge results client-side in `ProjectionService.For` (paliad-side). Still no library API change. #### Endpoints -- `GET /api/projects/{id}/scenarios` — list scenario names + which is active. -- `POST /api/projects/{id}/scenarios` — create a new named scenario (clones the choices of the source scenario name or empty if first). -- `PUT /api/projects/{id}/scenarios/{name}/active` — set as active scenario. -- `DELETE /api/projects/{id}/scenarios/{name}` — remove (cannot delete `'default'`; cannot delete the active one without picking another first). -- The existing `POST /api/projects/{id}/event-choices` learns to write against the active scenario (or accept an explicit `scenario_name` query param). +- `GET /api/projects/{id}/scenarios` — list project's scenarios + active. +- `GET /api/scenarios?abstract=true` — list current user's abstract scenarios. +- `POST /api/projects/{id}/scenarios` — create scoped scenario (body: name, spec). +- `POST /api/scenarios` — create abstract scenario (body: name, spec). +- `PATCH /api/scenarios/{id}` — rename / edit spec. +- `PUT /api/projects/{id}/active-scenario` — set active (body: scenario_id, or null to clear). +- `DELETE /api/scenarios/{id}` — remove (also clears any project.active_scenario_id pointing at it). +- `POST /api/scenarios/{id}/clone` — copy with new name. Supports cloning abstract → project-scoped and vice versa (the clone source gets `project_id` re-targeted). -#### UI +#### UI surfaces -- Project page sub-header gains a "Szenario" chip group above SmartTimeline: `[default ▾] + Neu`. -- Click chip → switch active → SmartTimeline re-renders against new choice set. -- "+ Neu" → modal: name + base ("Leer" or clone-from-current). -- SmartTimeline + Akte-mode Fristenrechner both read the active scenario's choices. +**Project page** (Akte-mode): +- "Szenarien" chip group above SmartTimeline: `[Aktuell] [mit CCR] [+ Neu]`. +- "Aktuell" = the live `project_event_choices` state (no active scenario). +- Click chip → set as active → SmartTimeline + Akte-Fristenrechner re-render. +- "+ Neu" modal: name + base (Leer / vom aktuellen Stand / abstrakte Vorlage importieren). +- Per-scenario "Bearbeiten" affordance opens the same per-card choice surface filtered to that scenario's spec. -#### Why this design +**/tools/verfahrensablauf** (abstract): +- Existing variant chips + per-card choices continue to drive a "live" abstract scenario carried in URL state. +- New "Speichern als Vorlage" button → modal: name → POST /api/scenarios (project_id=NULL). +- Sidebar list "Meine Vorlagen" lets the user reload a saved abstract scenario into the URL state. +- Existing share-via-URL stays — abstract scenarios are *also* shareable URLs; saving is opt-in for stickiness. -- **No new table** — the existing `project_event_choices` table already keys on `(project_id, submission_code, choice_kind)`. Adding `scenario_name` to the PK partitions naturally. -- **Backward compat** — every existing row gets `scenario_name='default'`; nothing breaks. -- **Library-side neutrality** — the package never sees scenarios. paliad's catalog/handler reads which scenario is active, builds the `CalcOptions` for it, and calls `Calculate`. youpc.org doesn't need scenarios at all (no projects). -- **Symmetry with Verfahrensablauf** — Verfahrensablauf stays URL-only (no projects, no persistence) — that surface is for abstract exploration, where URL-state IS the "scenario" (shareable, ephemeral, no name). +#### Library-side neutrality -#### Rejected: Option B (new `paliad.scenarios` table) +The package never sees the scenario. paliad's request builder unpacks the spec and calls `Calculate` with vanilla `CalcOptions`. youpc.org doesn't need scenarios at all (it stays on URL state). The scenarios feature is 100% paliad-side; the package stays clean. -Cleaner separation but duplicates the (project_id, choices) relationship. The composite-PK extension on `project_event_choices` is one column + one DEFAULT + one PK swap — strictly less work, no duplication. +#### What the spec is NOT -#### Rejected: Option C (URL-only, no persistence) +- Not a place to inject user-authored rules. m's clarification: "users should not add their own rules." The spec references existing rules by `submission_code`; it cannot create new ones. +- Not a place to add user-authored proceeding types. Same logic — the `code` field must resolve to an existing active `paliad.proceeding_types` row. +- Not a SmartTimeline event store. SmartTimeline reads actuals from `paliad.deadlines` / `appointments` / `project_events` as today; scenarios only influence the *projected* future. -Loses the "name it, switch to it" affordance m's framing implies. URL-only scenarios already exist on Verfahrensablauf; the project-page version needs more. +### Multi-proceeding peer compose — v2 path (m's "goal") -### Escalation to head (Q1) +m: *"Usually it would be 1 — but 2 is the goal. So people can create more complex scenarios."* -The package design works for any of A / B / C. The schema choice is paliad-side. **Recommendation = A** as designed above. If m flags a need for cross-project scenario sharing later (firm-shared templates), B becomes the cleaner base; A → B is a one-migration upgrade. +v2 lifts the engine to multi-peer: + +1. Frontend: the "+ Verfahren hinzufügen" button on the scenario editor adds a new entry to `proceedings[]` with `role: "peer"`, picks a proceeding code, optionally sets a `trigger_date_override`. +2. Backend: `ProjectionService.For` (or the request builder) iterates `proceedings[]`, calls `Calculate` per entry, tags each timeline event with its origin proceeding (new `Origin` field on `TimelineEntry`), and merges + sorts by date. +3. SmartTimeline renders peers as parallel lanes (reuses the existing lane infrastructure from t-paliad-175 Slice 4 — `LaneInfo` already supports parallel tracks). +4. No package change — the package processes one proceeding per call as today; multi-peer is a paliad-side orchestration. + +v2 is OUT OF SCOPE for v1 implementation but IN SCOPE for the spec/storage design. v1 silently ignores any `proceedings[]` entry with `role: "peer"`; v2 honours them. --- -## §6 User-authored rules design (Q2) +## §6 ~~User-authored rules design~~ — REMOVED by m's 2026-05-26 decision -### m's framing +> m's clarification: *"users should not add their own rules. I want them to setup a scenario with different existing proceeding types / submissions that trigger sequences."* + +The original Q2 (a) per-project rule additions via `paliad.deadline_rules.project_id` is **dropped**. No `project_id` column lands on `paliad.deadline_rules`. The corpus stays admin-curated (Slice 11 rule editor) — users compose, they don't author. §5 above replaces this design pillar: the scenario spec is the user's expression surface. + +The original §6 body is preserved below as historical context (struck-through) for future readers who want to understand what was considered and why it was rejected. + +--- + +~~### m's framing~~ > we can add proceeding types or specific submissions and then get the whole sequence with options for the steps @@ -818,38 +930,42 @@ Each slice is independently shippable on the paliad side. Slice F is a separate - `lp.Calculate(ctx, req, upc.NewCatalog(), upc.NewHolidayCalendar(), upc.NewCourtRegistry())` returns the same Timeline for every fixture as the paliad-side Catalog does. - A snapshot-drift test fails on intentional drift to prove the detector works. -### Slice D — scenarios persistence (Q1) +### Slice D — scenarios persistence: per-project (REVISED per m's 2026-05-26 picks) -**Scope** — paliad-side schema + endpoints + UI. +**Scope** — `paliad.scenarios` table + jsonb spec + project-page UI. **Files**: -- `internal/db/migrations/134_project_event_choices_scenario.up.sql` + `.down.sql` — add `scenario_name` + new PK + `active_scenario` on `paliad.projects`. -- `internal/services/scenario_service.go` (new) — Create/List/SetActive/Delete. -- `internal/handlers/scenarios.go` (new) — wire endpoints. -- `internal/services/event_choice_service.go` — learn to write/read against active scenario. -- `internal/services/projection_service.go` — consume active scenario's choices when building CalcOptions. -- `frontend/src/components/ScenarioChips.tsx` (new) + `frontend/src/client/projects-detail.ts` — UI surface. +- `internal/db/migrations/134_scenarios.up.sql` + `.down.sql` — create `paliad.scenarios` (project_id, name, spec jsonb, created_by, …) + `paliad.projects.active_scenario_id` FK + RLS via `paliad.can_see_project`. +- `internal/services/scenario_service.go` (new) — Create/List/Get/Patch/Clone/SetActive/Delete + JSON-schema validation of `spec` (every code/submission resolves; flags known). +- `internal/handlers/scenarios.go` (new) — wire the REST endpoints from §5. +- `internal/services/projection_service.go` — when `project.active_scenario_id` is set, build `CalcOptions` from the scenario's spec.primary entry instead of `paliad.project_event_choices`. +- `internal/services/event_choice_service.go` — unchanged (still owns the un-named "current" state); a TODO comment notes the v2 collapse if scenarios fully replace ad-hoc choices. +- `frontend/src/components/ScenarioChips.tsx` (new) + `frontend/src/client/projects-detail.ts` — chip group + create/edit modal + active switch. +- `frontend/src/components/ScenarioEditor.tsx` (new) — per-card choices + flags + anchor-overrides editor scoped to the scenario. **Exit criteria**: -- Project page shows scenario chips; create/switch/delete works. -- SmartTimeline + Akte-mode Fristenrechner respect active scenario. -- Existing projects function unchanged (everything in 'default'). +- Project page renders scenario chips; create/switch/delete works. +- SmartTimeline + Akte-Fristenrechner respect the active scenario. +- "Aktuell" (no active scenario) preserves today's behaviour reading from `project_event_choices`. +- Per-card choice editor writes into the scenario's spec.anchor_overrides / per_card_choices / skip_rules / flags. -### Slice E — user-authored rules (Q2) +### Slice E — abstract scenarios on /tools/verfahrensablauf (NEW, replaces ex-Slice E) -**Scope** — paliad-side schema + rule-editor extension. +**Scope** — abstract scenarios surface; reuses the same `paliad.scenarios` table with `project_id IS NULL`. **Files**: -- `internal/db/migrations/135_deadline_rules_project_id.up.sql` + `.down.sql` — nullable `project_id` column + composite uniqueness adjustment. -- `internal/services/deadline_rule_service.go` — learn `ListForProceeding(ctx, code, hint)` with merge semantics. -- `internal/services/rule_editor_service.go` — extend to scope writes to a project_id when set. -- `internal/handlers/rule_editor.go` — accept `?project=` scope param. -- `frontend/src/projects-detail.tsx` + `client/projects-detail.ts` — "Akte-spezifische Frist hinzufügen" affordance. +- `internal/services/scenario_service.go` — Slice D code already supports abstract scenarios; Slice E adds the `created_by`-scoped list endpoint + the clone-abstract-to-project / clone-project-to-abstract flows. +- `internal/handlers/scenarios.go` — extend with `?abstract=true` filter on List. +- `frontend/src/verfahrensablauf.tsx` + `client/verfahrensablauf.ts` — "Speichern als Vorlage" button + sidebar list "Meine Vorlagen" + load-saved-template → URL state. **Exit criteria**: -- A project-scoped rule appears in that project's SmartTimeline + Akte-Fristenrechner. -- The same rule does NOT appear in the public `/tools/fristenrechner` or in any other project. -- The snapshot generator's filter (`project_id IS NULL`) prevents leakage to youpc. +- /tools/verfahrensablauf gains save + list-my-templates affordances. +- A saved abstract scenario reloaded into URL state reproduces the live timeline byte-identically. +- Project page "+ Neu" modal supports "abstrakte Vorlage importieren" — copies the abstract scenario into the project's scope. + +### v2 — multi-proceeding peer compose (DEFERRED, no slice in this design) + +m's "goal": *"2 is the goal. So people can create more complex scenarios."* The spec already carries `proceedings[]` as an array; v2 lifts the engine + UI to honour `role: "peer"` entries. Out of scope for v1 — but the design holds because the schema doesn't need migration to support it. ### Slice F — youpc.org integration (separate repo) @@ -864,15 +980,17 @@ Each slice is independently shippable on the paliad side. Slice F is a separate **Out of scope of this design** — youpc's UX, youpc's auth gate, youpc's analytics, youpc's i18n. Those belong to a sibling task on the youpc.org repo. -### Slice ordering +### Slice ordering (revised) ``` A → B → C → F (youpc integration on C) ↘ - D + E (parallel, paliad-only, depend on B for catalog merge points) + D → E (paliad-only; E builds on D's table + schema) ``` -A is the prerequisite for everything. B is the prerequisite for C, D, E. C is the prerequisite for F. D and E are parallel after B. +A is the prerequisite for everything. B is the prerequisite for C and D. C is the prerequisite for F. D + E are sequential (E reuses D's table) but can ship as one PR if a worker prefers. + +Old "Slice E user-authored rules" is dropped per m's 2026-05-26 clarification (§6). New Slice E is the abstract scenarios surface on /tools/verfahrensablauf. --- @@ -888,7 +1006,7 @@ A is the prerequisite for everything. B is the prerequisite for C, D, E. C is th 4. **Catalog merge ambiguity (Slice E).** A project-scoped rule with the same `submission_code` as a global rule — does it override or coexist? **Mitigation**: the unique constraint `(proceeding_type_id, submission_code, project_id) NULLS NOT DISTINCT` allows ONE global + N project-scoped to coexist. The catalog returns both; the engine sees both rows. Ordering by `sequence_order` decides which renders first. Document this explicitly in the rule-editor UX (no silent override). -5. **Scenarios PK migration is destructive.** Slice D drops the existing PK and recreates it. **Mitigation**: small table (~50 rows in live data), standard `DROP CONSTRAINT` + `ADD CONSTRAINT` is atomic in a single transaction; down-migration restores. +5. **Scenarios spec validation drift.** Per m's revised design (§5), the `spec` jsonb references `proceeding_type.code` + `submission_code` + flag strings — all of which can be renamed by future migrations. A scenario saved against `upc.inf.cfi.inf.reply` would silently break if the submission_code changes. **Mitigation**: validate-on-load (not just on-write): when paliad reads a scenario, the service resolves every code/submission against the current corpus and surfaces a "Vorlage benötigt Aktualisierung" banner if anything's gone stale. The user can edit + re-save against the current corpus; the old spec is preserved until they do. 6. **youpc.org snapshot binary footprint.** ~110KB embedded JSON in youpc's binary. **Mitigation**: trivial. Not a risk in practice. @@ -919,25 +1037,17 @@ A is the prerequisite for everything. B is the prerequisite for C, D, E. C is th --- -## §13 Open questions for m (escalated via `mai instruct head`) +## §13 Open questions — RESOLVED 2026-05-26 -Per the inventor → head gate, the inventor does NOT call AskUserQuestion. The questions below are forwarded to `paliad/head`; m's answers come back via head's reply. +Originally Q1 + Q2 were escalated via `mai instruct head`. m switched to AskUserQuestion-mode mid-session; answers folded into §0.5. Original Q1+Q2 framings preserved below as historical context. -### Q1 — Scenarios storage model (material) +### Q1 — Scenarios storage model (material) — ANSWERED §0.5-Q4 -Recommendation: **Option A — extend `paliad.project_event_choices` with `scenario_name` text NOT NULL DEFAULT 'default' + add `paliad.projects.active_scenario`**. Reasoning §5. Alternatives: -- B: new `paliad.scenarios` table. -- C: URL-only scenarios, no persistence. +m picked **new `paliad.scenarios` table with jsonb spec** (option B in the original framing). Original recommendation (column extension) was retracted in favour of jsonb spec because m's Q1-Q3 picks (multi-proceeding peer compose as goal, per-project + abstract scope) made the jsonb shape strictly better. -m's call gates Slice D. The library's shape doesn't depend on which is picked. +### Q2 — User-authored rules scope (material) — REJECTED §0.5 -### Q2 — User-authored rules scope (material) - -Recommendation: **Option (a) — per-project rule additions via `paliad.deadline_rules.project_id` nullable column**. Reasoning §6. Alternatives: -- (a)+(b): also support new user-authored proceeding types. -- (c): defer entirely. - -m's call gates Slice E. If (b) is in scope, an additional Slice E2 is needed. +m clarified: *"users should not add their own rules."* Original Slice E dropped. New Slice E = abstract scenarios surface on /tools/verfahrensablauf. Composition replaces authoring. ### Q3 — Locked by m