Verlangt eine Vorlagen-Version eine Freigabe, laesst sich ein Lauf erst abschliessen, wenn ein Zweiter ihn freigegeben hat. Das braucht zweierlei, und beides zusammen: das neue Recht `freigeben` (ADMIN und REDAKTEUR, nicht TECHNIKER) und dass man nicht an dem Lauf gearbeitet hat. Als gearbeitet zaehlt: angelegt, einen Schritt gesetzt oder abgeschlossen. Eine Zuweisung allein nicht — waere die Regel hier strenger, blockierte sie kleine Teams ohne Gewinn. Auch ein Administrator kann sich nicht selbst freigeben, und das ist der ganze Sinn. Die Pruefung sitzt in `hatDaranGearbeitet` an einer Stelle, wie `laufMussVeraenderbarSein`; die wichtigste Haelfte — der Ersteller — haelt zusaetzlich ein CHECK in der Datenbank. Zurueckweisen verlangt eine Begruendung, setzt den Lauf auf IN_ARBEIT und nimmt eine erteilte Freigabe zurueck. Kein fuenfter LaufStatus: ein neuer Enum-Wert haette jeden Codepfad betroffen, der heute vier Faelle kennt. Sobald jemand abzuschliessen versucht und die Freigabe fehlt, bekommen die Freigabeberechtigten eine Mail — ein anderer Empfaengerkreis als bei jeder anderen Meldung, und ohne die, die gearbeitet haben. Die Aufforderung entsteht ausserhalb der Transaktion: innerhalb wuerde sie mit dem Fehler zurueckgerollt, und dann wartet ein Lauf auf etwas, von dem niemand weiss. Ein partieller Unique-Index haelt sie bei mehreren Versuchen auf einmal. Die Pflicht haengt an der Version, nicht an der Vorlage: eine veroeffentlichte Version ist unveraenderlich, ein laufender Lauf behaelt also seine Regel. Beim Bearbeiten und Duplizieren wandert sie mit — das war die unmittelbare Lehre aus #039, wo die tiefe Kopie zwei Felder stillschweigend verworfen hatte. 1526 Vitest-Tests (vorher 1496), 115 E2E-Ablaeufe gruen. Klickprobe am gebauten Stand mit elf Pruefungen; drei rote Punkte darin waren meine eigenen Testfehler, einer davon lehrreich: ein reload() direkt nach einer Server Action trifft den Router-Cache von Next und zeigt kurz den alten Stand. Closes #40 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
128 lines
8.1 KiB
Markdown
128 lines
8.1 KiB
Markdown
# #040 M24 – Vier-Augen-Freigabe
|
||
|
||
**Status:** erledigt
|
||
**Im Tracker:** –
|
||
**Meilenstein:** v0.3.0
|
||
**Labels:** phase-3, backend, frontend
|
||
**Geschätzt:** 8 h **Tatsächlich:** 3 h 30 min
|
||
|
||
## Ziel
|
||
|
||
Manche Arbeiten soll nicht derselbe abschließen, der sie gemacht hat: ein Firewall-Rollout, ein
|
||
Restore-Test, das Löschen eines Kontos. Heute kann jeder `TECHNIKER` jeden Lauf abschließen und
|
||
archivieren.
|
||
|
||
Gebraucht wird: eine Vorlage kann sagen „dieser Lauf braucht eine Freigabe“, und dann schließt ihn
|
||
ein **Zweiter** ab — nachweisbar im Protokoll.
|
||
|
||
## Zu entscheiden
|
||
|
||
- [x] **Freigabe am Lauf oder am Schritt?** Empfehlung: **am Lauf**, gesetzt von der Vorlage.
|
||
Am Schritt wäre feiner und verführt zu Checklisten, in denen an fünf Stellen jemand warten
|
||
muss; am Lauf ist es eine Aussage über die Arbeit als Ganzes und erklärt sich von selbst.
|
||
Schritt-Freigaben bleiben möglich, wenn sich der Bedarf zeigt — `VorlagenSchritt.konfig` ist
|
||
ein Json.
|
||
- [x] **Wer darf freigeben?** Empfehlung: ein eigenes Recht `freigeben`, standardmäßig bei `ADMIN`
|
||
und `REDAKTEUR` — **nicht** bei `TECHNIKER`. Begründung: die Vier-Augen-Regel soll nicht
|
||
davon abhängen, dass zufällig ein zweiter Techniker greifbar ist; und wer sie umgehen will,
|
||
braucht dann ein zweites Konto mit mehr Rechten, was im Protokoll steht.
|
||
- [x] **Darf der Bearbeiter sich selbst freigeben?** **Nein**, und das ist der ganze Sinn. Geprüft
|
||
wird gegen `Schrittergebnis.aktualisiertVonId` **und** `Lauf.erstelltVonId`: wer irgendetwas
|
||
daran getan hat, gibt nicht frei. Ein `ADMIN`, der allein im Haus ist, kommt damit nicht
|
||
weiter — das ist eine bewusste Härte, und die Anleitung sagt es.
|
||
- [x] **Was, wenn die Freigabe verweigert wird?** Empfehlung: ein Zustand „zurückgewiesen“ mit
|
||
Pflichtnotiz. Der Lauf geht zurück auf `IN_ARBEIT`, die Notiz steht im Protokoll und an der
|
||
Abarbeitungsansicht. Kein eigener `LaufStatus` — das wäre eine Änderung an einem Enum, das
|
||
vier Jahre halten soll; es genügt ein Feld plus Protokolleintrag.
|
||
|
||
## Aufgaben
|
||
|
||
- [x] Migration: `freigabePflicht Boolean` an `VorlagenVersion`, dazu `freigegebenVonId`,
|
||
`freigegebenAm` und `freigabeNotiz` am `Lauf`; CHECK, dass `freigegebenVonId` und
|
||
`freigegebenAm` zusammen gesetzt sind (wie bei `abgeschlossenAm`)
|
||
- [x] CHECK: `freigegebenVonId` ist **nicht** `erstelltVonId` — die Regel gehört in die Datenbank,
|
||
nicht nur in den Dienst (CLAUDE.md)
|
||
- [x] Recht `freigeben` in `RECHTE`, Rollenmatrix-Test erweitern
|
||
- [x] Dienst: `laufFreigeben(laufId, benutzer, notiz?)` und `laufZurueckweisen(laufId, benutzer,
|
||
notiz)`; Abschluss verlangt die Freigabe, wenn die Version sie fordert
|
||
- [x] Die Prüfung „wer hat daran gearbeitet“ an **einer** Stelle, wie `laufMussVeraenderbarSein`
|
||
- [x] Benachrichtigung: ein neuer `Meldeanlass` `LAUF_FREIGABE_OFFEN` an die Rollen mit dem Recht —
|
||
sonst wartet ein Lauf, von dem niemand weiß
|
||
- [x] Oberfläche: Hinweis am Lauf („wartet auf Freigabe durch einen Zweiten“), Knöpfe für Freigeben
|
||
und Zurückweisen, Notizfeld; die Übersicht bekommt einen Filter „wartet auf meine Freigabe“
|
||
- [x] Editor: Häkchen „dieser Lauf braucht eine Freigabe“ an der Version
|
||
- [x] Druckansicht und PDF nennen Freigeber und Zeitpunkt
|
||
- [x] Anleitung: Kapitel 02 (Läufe abarbeiten) und 04 (Vorlagen), mit dem Hinweis, dass ein
|
||
Einzelkämpfer sich nicht selbst freigeben kann
|
||
|
||
## Akzeptanzkriterien
|
||
|
||
- [x] Test: ein Lauf mit Freigabepflicht lässt sich nicht abschließen, solange keine Freigabe steht
|
||
- [x] Test: der Bearbeiter kann **nicht** freigeben — auch nicht als `ADMIN`
|
||
- [x] Test: die Datenbank weist es ebenfalls ab (CHECK), nicht nur der Dienst
|
||
- [x] Test: eine Rolle ohne `freigeben` wird abgewiesen, über jede Server Action
|
||
- [x] Test: Zurückweisen verlangt eine Notiz und setzt den Lauf auf `IN_ARBEIT`
|
||
- [x] Test: die Meldung geht an die Rollen mit dem Recht und nicht an alle
|
||
- [x] Test: ein Lauf **ohne** Freigabepflicht verhält sich unverändert — bestehende Läufe und
|
||
Vorlagen merken nichts
|
||
- [x] Am Handy kein horizontales Scrollen
|
||
|
||
## Beim Umsetzen dazugekommen
|
||
|
||
- **Die vier Entscheidungen sind so gefallen wie empfohlen.** Zwei haben sich im Code als besonders
|
||
tragend erwiesen:
|
||
- **Die Freigabe hängt an der Version, nicht an der Vorlage.** Eine veröffentlichte Version ist
|
||
unveränderlich — ein laufender Lauf kann seine Regel damit nicht unter den Füßen verlieren. In
|
||
der Versionsliste steht an jeder Fassung, ob sie eine Freigabe verlangt.
|
||
- **Kein fünfter `LaufStatus` für „zurückgewiesen“.** Der Lauf geht auf `IN_ARBEIT`, die
|
||
Begründung steht in `freigabeNotiz` und im Protokoll. Ein neuer Enum-Wert hätte jeden Codepfad
|
||
betroffen, der heute vier Fälle kennt.
|
||
- **Als „gearbeitet“ zählt dreierlei** — angelegt, ein Schrittergebnis gesetzt, abgeschlossen.
|
||
Der **Zuständige** gilt bewusst nicht als Bearbeiter: eine Zuweisung ist noch keine Arbeit, und
|
||
wäre die Regel hier strenger, blockierte sie kleine Teams ohne Gewinn. Die Prüfung sitzt in
|
||
`hatDaranGearbeitet`, an einer Stelle — wie `laufMussVeraenderbarSein`.
|
||
- **Die Aufforderung entsteht außerhalb der Transaktion**, und das war die einzige knifflige Stelle:
|
||
der Abschlussversuch scheitert mit einem eigenen Fehlertyp `FreigabeFehlt`, den
|
||
`laufAbschliessen` außen fängt, um die Meldung einzureihen und dann weiterzuwerfen. Innerhalb
|
||
würde sie mit dem Fehler zurückgerollt — und dann wartet ein Lauf auf etwas, von dem niemand
|
||
weiß. Dafür musste die Transaktion in eine eigene Funktion (`abschlussVersuchen`).
|
||
- **Ein anderer Empfängerkreis als bei jeder anderen Meldung**: nicht der Zuständige, sondern jeder
|
||
mit dem Recht `freigeben` — ohne die, die gearbeitet haben. Eine Aufforderung zu etwas, das der
|
||
Dienst abweist, ist schlimmer als keine.
|
||
- **Ein partieller Unique-Index** (`Zustellung_freigabe_einmal`) hält die Mahnung bei einem Lauf
|
||
und Empfänger auf einmal. Ein Techniker versucht es mehrmals; ohne den Index wäre jede Mail für
|
||
sich korrekt und in Summe eine Belästigung — derselbe Fall wie „überfällig“ in #034, nur
|
||
schneller erreicht.
|
||
- **Die Freigabepflicht wandert bei „Bearbeiten“ und „Duplizieren“ mit.** Das war kein Zufall,
|
||
sondern die unmittelbare Lehre aus #039: dort hatte die tiefe Kopie `nurFuerAbteilungen` und
|
||
`bedingung` stillschweigend verworfen. Eine Vorlage soll ihre Freigabepflicht nicht durch eine
|
||
Textkorrektur verlieren.
|
||
|
||
## Was die Klickprobe gezeigt hat
|
||
|
||
Elf Prüfungen am gebauten Stand, alle grün — von „Freigabe-Karte sichtbar“ über „Zurückweisen
|
||
verlangt eine Begründung“ bis „Häkchen aus der Vorversion übernommen“ und 390 px.
|
||
|
||
**Drei rote Punkte waren meine eigenen Testfehler**, und der dritte ist festzuhalten:
|
||
|
||
1. Ein Selektor traf den **versteckten** Protokolltext „Lauf abgeschlossen“ statt der Erfolgsmeldung.
|
||
2. Ein Suchmuster `/freigeb/` traf nichts: in „Freigabe“ und „freigegeben“ kommt diese Zeichenfolge
|
||
gar nicht vor. Dreimal gesucht, bis es auffiel.
|
||
3. **Ein `reload()` direkt nach einer Server Action zeigt kurz den alten Stand.** Die Action
|
||
revalidiert den Pfad, und der Benutzer sieht das Ergebnis ohne Neuladen — ein Reload im selben
|
||
Kontext trifft dagegen den Router-Cache von Next. Das ist Next und kein Fehler dieser Anwendung,
|
||
aber es ist auch nicht, was ein Mensch tut. Die Probe prüft jetzt das echte Verhalten.
|
||
|
||
Nachgewiesen, bevor etwas geändert wurde: in einem frischen Browser-Kontext stand alles richtig
|
||
da, und die Datenbank trug Freigeber, Zeitpunkt und Notiz.
|
||
|
||
## Was offen bleibt
|
||
|
||
- **Freigaben an einzelnen Schritten** gibt es nicht, bewusst. Am Lauf ist es eine Aussage über die
|
||
Arbeit als Ganzes und erklärt sich von selbst; am Schritt verführte es zu Checklisten, in denen an
|
||
fünf Stellen jemand warten muss. `VorlagenSchritt.konfig` ist ein Json — wenn sich der Bedarf
|
||
zeigt, ist der Platz da.
|
||
- **Ein Einzelkämpfer kommt nicht weiter**: wer allein ist und an dem Lauf gearbeitet hat, kann ihn
|
||
nicht freigeben, auch nicht als Administrator. Das ist die Härte, die die Regel ausmacht, und die
|
||
Anleitung sagt es ausdrücklich.
|