# #039 M23 – Bedingte Schritte und Abschnitte **Status:** erledigt **Im Tracker:** – **Meilenstein:** v0.3.0 **Labels:** phase-3, backend, frontend **Geschätzt:** 12 h **Tatsächlich:** 5 h 20 min ## Ziel Eine Checkliste enthält heute **alles**, und was nicht zutrifft, wird von Hand auf `NICHT_ZUTREFFEND` gesetzt. Der echte Fall steht im Seed: „Mitarbeiter-Onboarding“ trägt **beide** Konto-Abschnitte — „Konto anlegen (Active Directory)“ mit 5 Schritten und „Konto anlegen (Entra ID)“ mit 4 —, obwohl in jedem einzelnen Lauf nur einer gilt. Die Variable `konto_typ` sagt es bereits; niemand wertet sie aus. Neun Schritte, die ein Techniker bei jedem Onboarding wegklickt. Die Abschnitts-Aktion aus #013 macht es erträglich, aber sie ist ein Pflaster: der Fortschrittsbalken zählt die Schritte mit, und im Archiv steht später eine Checkliste mit einem Abschnitt, den niemand je gebraucht hat. Setzt [#031](031-abteilungsabschnitte.md) voraus: `geltendeAbschnitte` und `nurGeltende` sind die Stelle, an der das sitzt. ## Zu entscheiden - [x] **Wann wird ausgewertet?** Das ist die Entscheidung, an der alles hängt. Für Abteilungen (#031) wird beim **Start festgeschrieben** — die Abteilung des Laufs steht dann fest, und ein archivierter Lauf muss genau die Schritte zeigen, die damals galten. Für Bedingungen reicht das **nicht**: eine Bedingung auf einer Variable (`konto_typ`) ließe sich beim Start auswerten, eine Bedingung auf einem **Schrittergebnis** („wenn Schritt 3 = nein, dann Abschnitt B“) nicht — die Antwort entsteht erst während der Arbeit. Empfehlung: **laufend auswerten, Ergebnis mitschreiben.** Die Auswertung läuft bei jeder Änderung an einem Schrittergebnis; was dabei herauskommt, wird in den Lauf geschrieben (wie `geltendeAbschnitte`). Damit gilt für das Archiv weiter „der Lauf weiß, was galt“, und der Ereignisstrom aus #033 trägt die Änderung ohnehin schon zur zweiten Sitzung. - [x] **Woran darf eine Bedingung hängen?** Empfehlung: an einer **Variable** des Laufs und an einem **Schrittergebnis** derselben Version. Nicht an Gerätetyp, Abteilung oder Rolle — für Abteilungen gibt es #031, und je mehr Quellen, desto schwerer zu erklären, warum ein Schritt fehlt. - [x] **Wie wird sie gespeichert?** Empfehlung: ein Json-Feld `bedingung` an `VorlagenAbschnitt` und `VorlagenSchritt`, mit einem engen Schema — `{ art: "variable", schluessel, operator, wert }` oder `{ art: "schritt", schrittId, zustand }`. Json, weil `konfig` es schon so macht und ein neuer Operator dann keine Migration braucht. **Eine Bedingung, kein Ausdrucksbaum.** Kein `und`/`oder`, keine Verschachtelung: das wäre eine kleine Sprache, die niemand im Editor pflegen kann, und ein Abschnitt, der aus unerklärlichen Gründen fehlt, ist schlimmer als einer zu viel. - [x] **Was passiert mit einem Schrittergebnis, das schon Daten trägt**, wenn die Bedingung umschlägt? Empfehlung: die Zeile **bleibt** und wird nur ausgeblendet. Löschen hieße, eine Notiz zu verlieren, die jemand geschrieben hat; und schlägt die Bedingung zurück, ist sie wieder da. ## Aufgaben - [x] Migration: `bedingung Json?` an `VorlagenAbschnitt` und `VorlagenSchritt`, dazu ein CHECK, der sicherstellt, dass eine Bedingung auf einen Schritt **derselben Version** zeigt (sonst zeigt sie nach dem Veröffentlichen ins Leere) - [x] `src/lib/bedingungen.ts`, framework-frei: Schema, Auswertung, und die Liste der Schritte und Abschnitte, die gelten. **Dieselbe Stelle für alle drei Schichten**, wie `src/lib/geltende-abschnitte.ts` - [x] Auswertung bei jedem Schreiben an einem `Schrittergebnis`; das Ergebnis in den Lauf - [x] Abschlussregel: ein ausgeblendeter Pflichtschritt hält den Abschluss **nicht** auf (dieselbe Regel wie bei #031, dieselbe Funktion) - [x] Fortschrittsbalken und Zählwerte rechnen nur mit dem, was gilt - [x] Editor: Bedingung an Abschnitt und Schritt setzen, mit Auswahl der Variable bzw. des Schritts **aus derselben Version**; eine Vorschau, die sagt „gilt, wenn …“ - [x] Warnung im Editor bei einer Bedingung auf einen Schritt, der **nach** dem bedingten steht — sie wäre beim Abarbeiten noch offen, und der Abschnitt erschiene erst rückwirkend - [x] Abarbeitung: ausgeblendete Abschnitte verschwinden, ohne die Nummerierung zu verschieben (`01 · Vorbereitung` kommt aus der Position und wird nie gespeichert) - [x] Druckansicht und Archiv zeigen, was galt - [x] Export/Import: die Bedingung wandert mit, und ein Verweis auf einen Schritt wird dabei auf die **neue** Kennung umgeschrieben — sonst zeigt sie in der zweiten Installation ins Leere - [x] Seed: „Mitarbeiter-Onboarding“ bekommt die Bedingung auf `konto_typ`, die beiden Konto-Abschnitte bleiben drin und schließen sich gegenseitig aus. Das ist die Gegenprobe am echten Fall. - [x] Anleitung Kapitel 04 (Vorlagen): wann eine Bedingung das richtige Mittel ist und wann eine zweite Vorlage ## Akzeptanzkriterien - [x] Test: ein Lauf mit `konto_typ = AD` hat die 5 AD-Schritte und **keinen** der 4 Entra-Schritte - [x] Test: dasselbe umgekehrt, und bei nicht gesetzter Variable gelten **beide** — eine Bedingung, die nicht entschieden werden kann, blendet nichts aus - [x] Test: eine Bedingung auf einem Schrittergebnis schlägt um, **während** der Lauf läuft, und der Abschnitt erscheint - [x] Test: ein ausgeblendeter Pflichtschritt hält den Abschluss nicht auf - [x] Test: ein Schrittergebnis mit Notiz bleibt stehen, wenn seine Bedingung umschlägt, und ist danach wieder da - [x] Test: Export und Import einer Vorlage mit Bedingungen ergeben dieselbe Struktur, und der Schritt-Verweis zeigt auf den richtigen Schritt der **neuen** Version - [x] Test: ein archivierter Lauf zeigt genau die Schritte, die bei seinem Abschluss galten - [x] Am Handy kein horizontales Scrollen ## Beim Umsetzen dazugekommen - **Die Entscheidungen aus dem Issue sind so gefallen wie empfohlen**, und zwei davon haben sich im Code als richtig erwiesen: - **Laufend auswerten, nicht beim Start festschreiben.** Eine Bedingung auf einer Variable ließe sich wie #031 beim Start festhalten; eine auf einem Schrittergebnis nicht — und genau die ist der interessante Fall (ein Abschnitt „Nacharbeit“, der erscheint, wenn der Restore-Test fehlschlägt). - **Nicht entscheidbar heißt: gilt.** Ein Lauf ohne `konto_typ` zeigt beide Konto-Abschnitte — genau wie vor diesem Meilenstein. Eine erfundene Antwort wäre die Verschlechterung. - **`OFFEN` ist eine Antwort, keine Lücke.** Der Unterschied zur Variable musste scharf gezogen werden: eine nicht gesetzte Variable ist nicht entscheidbar, ein offener Schritt dagegen klar „noch nicht erfüllt“. Wäre `OFFEN` unentscheidbar, wäre ein bedingter Abschnitt von Anfang an sichtbar — das Gegenteil des Zwecks. - **Verwiesen wird über Titel-Pfade, nicht über Kennungen**, und das war die größte Einzelerkenntnis. **Vier** Wege schreiben eine Struktur neu und geben dabei neue Kennungen: Import, Export, Seed und `entwurfSpeichern`. Eine Bedingung auf einer Kennung überlebt keinen davon. `bedingungAufloesen` ist für alle vier dieselbe Stelle; dass eine Bedingung auf **dieselbe Version** zeigt, hält zusätzlich ein Trigger. - **Ein struktureller Test bewacht die drei Schichten.** Abschlussregel, Abarbeitung und Druck müssen alle `geltendeStruktur` rufen; vergisst eine es, sieht der Techniker andere Schritte als der Ausdruck. Der Test liest die drei Dateien — dieselbe Art Prüfung wie „jede Seite prüft selbst“ (#038). - **Zwei Schemas für dieselbe Form liefen fast auseinander.** Austauschformat und Editor-Struktur brauchen die Pfad-Form; ich hatte sie zweimal geschrieben, und TypeScript meldete es erst, als die beiden Typen aufeinandertrafen. Jetzt steht `PfadBedingungSchema` in `src/lib/bedingungen.ts`, und `vorlagen-format.ts` importiert es. ## Zwei stille Datenverluste, gefunden bei der Klickprobe Beides hätte keine Suite gezeigt, und das zweite war **älter als dieser Meilenstein**: - **Der Import verwarf `nurFuerAbteilungen`.** Der Export schreibt die Abteilungseinschränkung seit [#031](031-abteilungsabschnitte.md), das Schema nimmt sie an — und der Import setzte sie nicht. Eine Vorlage, die jemand in eine zweite Installation brachte, verlor ihre Abteilungsabschnitte: sie galten dort für **alle**. Also mehr Schritte statt weniger, und niemand merkt es. - **Die tiefe Kopie verwarf sie ebenfalls — und seit heute auch die Bedingung.** „Bearbeiten“ einer Vorlage erzeugt eine tiefe Kopie als Entwurf (#009); `vorlageDuplizieren` dasselbe. Beide hatten die Kopie einzeln ausgeschrieben, und beide vergaßen dieselben Felder. Damit verlor **jede Bearbeitung** alle Abteilungseinschränkungen. **Die Ursache war ein von Hand getippter Zwischentyp.** `VersionMitInhalt` nannte weniger Felder, als die Abfrage liefert; die Felder waren da, der Code konnte sie nicht sehen. Jetzt ist der Typ aus Prisma abgeleitet — ein neues Feld am Modell ist dort automatisch vorhanden. Gefunden hat es die **Klickprobe am gebauten Stand**: der Editor zeigte „Bedingung gesetzt“ in der veröffentlichten Version, und nach „Bearbeiten“ war sie weg. Das ist genau der Grund, warum diese Probe in `CLAUDE.md` steht. Bewacht wird es jetzt von einem Test, der Kopie und Original **Feld für Feld** vergleicht — nicht stichprobenweise. So fällt auch das nächste Feld auf, das jemand ergänzt und beim Kopieren vergisst. - **Ein Mangel an der neuen Komponente**, aufgefallen beim Klicken: die Auswahlfelder standen in einem umhüllenden `