# #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.