docs(litigation-planner): fold m's AskUserQuestion picks — new paliad.scenarios table + jsonb spec, no user-authored rules (t-paliad-292)
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

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.
This commit is contained in:
mAi
2026-05-26 10:21:23 +02:00
parent a5b96d71eb
commit a1646b41a0

View File

@@ -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=<id>` 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