feat(vorlagen): bedingte Abschnitte und Schritte (#039)
Ein Abschnitt oder ein einzelner Schritt erscheint nur, wenn seine Bedingung zutrifft — abhaengig von einer Variable des Laufs oder vom Zustand eines anderen Schritts. Der echte Fall steht im Seed: "Mitarbeiter-Onboarding" traegt beide Konto-Abschnitte, fuenf AD-Schritte und vier fuer Entra ID, und `konto_typ` sagt beim Start, welcher gilt. Bisher klickte ein Techniker die anderen neun bei jedem Onboarding weg. Entscheidungen, alle im Issue begruendet: - Laufend ausgewertet, nicht beim Start festgeschrieben. Fuer die Abteilungsabschnitte (#031) ist Festschreiben richtig; fuer Bedingungen nicht — die Antwort auf "ist Pruefen fehlgeschlagen?" entsteht erst waehrend der Arbeit. Ein struktureller Test prueft, dass alle drei Schichten (Abschlussregel, Abarbeitung, Druck) die Auswertung rufen. - Nicht entscheidbar heisst: gilt. Ein Lauf ohne konto_typ zeigt beide Abschnitte — genau wie vorher. Ein Abschnitt zu viel ist sichtbar und korrigierbar, ein fehlender faellt niemandem auf. - OFFEN ist eine Antwort, keine Luecke: waere es unentscheidbar, waere ein bedingter Abschnitt von Anfang an sichtbar. - Eine Bedingung, kein Ausdrucksbaum. Kein und, kein oder. - Verwiesen wird ueber Titel-Pfade, nicht ueber Kennungen: vier Wege schreiben eine Struktur neu und geben neue Kennungen. Zwei stille Datenverluste gefunden, der zweite aelter als dieser Meilenstein: - Der Import verwarf `nurFuerAbteilungen`. Der Export schreibt es seit #031, das Schema nimmt es an, der Import setzte es nicht — eine exportierte Vorlage verlor ihre Abteilungsabschnitte und galt dort fuer alle. - Die tiefe Kopie verwarf es ebenfalls, womit jedes "Bearbeiten" einer Vorlage alle Einschraenkungen verlor. Ursache war ein von Hand getippter Zwischentyp, der weniger Felder nannte als die Abfrage liefert; jetzt aus Prisma abgeleitet, beide Kopierwege teilen eine Stelle, und ein Test vergleicht Kopie und Original Feld fuer Feld. Gefunden hat beides die Klickprobe am gebauten Stand, nicht die Suite: im Editor stand "Bedingung gesetzt", nach "Bearbeiten" war sie weg. Dreizehn Gegenproben liefen, alle wurden rot — und der teuerste Befund lag ausserhalb von allen. 1499 Vitest-Tests (vorher 1457), 115 E2E-Ablaeufe gruen. Closes #39 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2f247b3617
commit
a2330ef682
@@ -1,10 +1,10 @@
|
||||
# #039 M23 – Bedingte Schritte und Abschnitte
|
||||
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.3.0
|
||||
**Labels:** phase-3, backend, frontend
|
||||
**Geschätzt:** 12 h **Tatsächlich:** –
|
||||
**Geschätzt:** 12 h **Tatsächlich:** 5 h 20 min
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -23,7 +23,7 @@ Stelle, an der das sitzt.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
- [ ] **Wann wird ausgewertet?** Das ist die Entscheidung, an der alles hängt.
|
||||
- [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.
|
||||
@@ -36,60 +36,129 @@ Stelle, an der das sitzt.
|
||||
Ä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.
|
||||
- [ ] **Woran darf eine Bedingung hängen?** Empfehlung: an einer **Variable** des Laufs und an
|
||||
- [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.
|
||||
- [ ] **Wie wird sie gespeichert?** Empfehlung: ein Json-Feld `bedingung` an `VorlagenAbschnitt`
|
||||
- [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.
|
||||
- [ ] **Was passiert mit einem Schrittergebnis, das schon Daten trägt**, wenn die Bedingung
|
||||
- [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
|
||||
|
||||
- [ ] Migration: `bedingung Json?` an `VorlagenAbschnitt` und `VorlagenSchritt`, dazu ein CHECK, der
|
||||
- [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)
|
||||
- [ ] `src/lib/bedingungen.ts`, framework-frei: Schema, Auswertung, und die Liste der Schritte und
|
||||
- [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`
|
||||
- [ ] Auswertung bei jedem Schreiben an einem `Schrittergebnis`; das Ergebnis in den Lauf
|
||||
- [ ] Abschlussregel: ein ausgeblendeter Pflichtschritt hält den Abschluss **nicht** auf (dieselbe
|
||||
- [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)
|
||||
- [ ] Fortschrittsbalken und Zählwerte rechnen nur mit dem, was gilt
|
||||
- [ ] Editor: Bedingung an Abschnitt und Schritt setzen, mit Auswahl der Variable bzw. des Schritts
|
||||
- [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 …“
|
||||
- [ ] Warnung im Editor bei einer Bedingung auf einen Schritt, der **nach** dem bedingten steht —
|
||||
- [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
|
||||
- [ ] Abarbeitung: ausgeblendete Abschnitte verschwinden, ohne die Nummerierung zu verschieben
|
||||
- [x] Abarbeitung: ausgeblendete Abschnitte verschwinden, ohne die Nummerierung zu verschieben
|
||||
(`01 · Vorbereitung` kommt aus der Position und wird nie gespeichert)
|
||||
- [ ] Druckansicht und Archiv zeigen, was galt
|
||||
- [ ] Export/Import: die Bedingung wandert mit, und ein Verweis auf einen Schritt wird dabei auf die
|
||||
- [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
|
||||
- [ ] Seed: „Mitarbeiter-Onboarding“ bekommt die Bedingung auf `konto_typ`, die beiden
|
||||
- [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.
|
||||
- [ ] Anleitung Kapitel 04 (Vorlagen): wann eine Bedingung das richtige Mittel ist und wann eine
|
||||
- [x] Anleitung Kapitel 04 (Vorlagen): wann eine Bedingung das richtige Mittel ist und wann eine
|
||||
zweite Vorlage
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: ein Lauf mit `konto_typ = AD` hat die 5 AD-Schritte und **keinen** der 4 Entra-Schritte
|
||||
- [ ] Test: dasselbe umgekehrt, und bei nicht gesetzter Variable gelten **beide** — eine Bedingung,
|
||||
- [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
|
||||
- [ ] Test: eine Bedingung auf einem Schrittergebnis schlägt um, **während** der Lauf läuft, und der
|
||||
- [x] Test: eine Bedingung auf einem Schrittergebnis schlägt um, **während** der Lauf läuft, und der
|
||||
Abschnitt erscheint
|
||||
- [ ] Test: ein ausgeblendeter Pflichtschritt hält den Abschluss nicht auf
|
||||
- [ ] Test: ein Schrittergebnis mit Notiz bleibt stehen, wenn seine Bedingung umschlägt, und ist
|
||||
- [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
|
||||
- [ ] Test: Export und Import einer Vorlage mit Bedingungen ergeben dieselbe Struktur, und der
|
||||
- [x] Test: Export und Import einer Vorlage mit Bedingungen ergeben dieselbe Struktur, und der
|
||||
Schritt-Verweis zeigt auf den richtigen Schritt der **neuen** Version
|
||||
- [ ] Test: ein archivierter Lauf zeigt genau die Schritte, die bei seinem Abschluss galten
|
||||
- [ ] Am Handy kein horizontales Scrollen
|
||||
- [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 `<label>`, und damit schluckte ihr zugänglicher Name den Text der gewählten
|
||||
Option mit („VariableKontotyp“). Für einen Screenreader ist das eine Bezeichnung, die sich beim
|
||||
Umschalten ändert. Jetzt `htmlFor`/`id` — derselbe Fehler wie bei der Adresse im Verzeichnis-Formular
|
||||
(#016).
|
||||
|
||||
## Was offen bleibt
|
||||
|
||||
- **Kein `und`/`oder`**, bewusst. Wer zwei Bedingungen braucht, nimmt zwei Abschnitte.
|
||||
- **Ein Baustein-Schritt hat keine Bedingung** (der Abschnitt, in dem er steht, darf eine haben): ein
|
||||
Baustein ist wiederverwendbar, und eine Bedingung auf einer Variable der Zielvorlage wäre dort
|
||||
nicht entscheidbar.
|
||||
- **Zwei Schritte mit demselben Titel im selben Abschnitt** sind über den Titel-Pfad nicht
|
||||
unterscheidbar; die Bedingung zeigt dann auf den ersten. Benannt in `src/lib/bedingungen.ts` —
|
||||
zwei gleich benannte Schritte nebeneinander kann auch ein Mensch nicht unterscheiden.
|
||||
|
||||
Reference in New Issue
Block a user