feat(laeufe): Service, Abarbeitung, Kommentare und Archivschutz
Prüfung / pruefung (push) Canceled after 0s
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s

Ein Lauf wird vorbereitet, abgearbeitet, abgeschlossen und archiviert. Der
Archiv-Schreibschutz sitzt in genau einer Funktion und wird vor jeder Aenderung
an Kopfdaten, Schrittergebnis und Kommentar gerufen; abgeschlossen bleibt
aenderbar, archiviert nicht.

Die Abarbeitungsansicht speichert jeden Schritt einzeln, frischt sich alle zehn
Sekunden auf und pausiert im Hintergrund. Sie traegt in der Polling-Antwort
keine Befehle und laedt sich auch nicht von selbst neu, solange ein generiertes
Passwort auf der Seite steht: beides tauschte es dem Techniker zwischen Lesen
und Einfuegen aus.

Der unabhaengige Review fand zwei Blocker: ein Befehl konnte zwei verschiedene
Passwoerter tragen (Konto bekommt A, Benutzer erfaehrt B), und „nicht
zutreffend“ liess sich nicht zuruecknehmen, waehrend die Oberflaeche
behauptete, nichts sei gewaehlt. Beides behoben und in Tests festgehalten.

Closes #13

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-25 18:55:59 +02:00
co-authored by Claude Opus 5
parent eec5de404a
commit ed6e6b81eb
44 changed files with 7440 additions and 61 deletions
+24
View File
@@ -68,6 +68,15 @@ Zwei Regeln tragen die Nachvollziehbarkeit:
Beides ist durch Tests abgedeckt, nicht durch Disziplin.
**Abgeschlossen ist nicht archiviert**, und der Unterschied ist Absicht. „Abgeschlossen“ ist ein
Arbeitszustand: fertig für jetzt. Eine nachgetragene Notiz, ein Kommentar mit dem, was beim
Nachfassen herauskam, eine korrigierte Seriennummer — das passiert danach, und zwar oft. Würde
schon der Abschluss sperren, bräuchte jede Kleinigkeit einen Administrator, und die Technikerin
würde Läufe schlicht nicht mehr abschließen; das Archiv wäre danach nicht genauer, sondern leerer.
Unveränderlich wird ein Lauf durch das **Archivieren**, eine ausdrückliche zweite Handlung. Damit
„abgeschlossen“ trotzdem nicht lügt, nimmt der Abschluss sich selbst zurück, sobald eine Änderung
einen Pflichtschritt wieder offen lässt.
Der Aufbau einer Version wird **als Ganzes** gespeichert, nicht Schritt für Schritt: der Editor
hält den Baum im Speicher und sortiert per Drag & Drop, ein Teil-Update wäre dort ständig mit dem
Sortieren im Streit. Dasselbe Zod-Schema (`src/lib/vorlagen-format.ts`) beschreibt den Entwurf im
@@ -176,6 +185,21 @@ Das **generierte Passwort** entsteht erst beim Ersetzen und wird nirgends zurüc
keinen Weg, es außerhalb des fertigen Texts zu bekommen. Gäbe es ein Feld dafür, wäre die
Versuchung groß, es „nur fürs Protokoll“ mitzuschreiben.
## 5d. Warum die Aktualisierung keine Befehle mitliefert
Die Abarbeitungsansicht fragt alle zehn Sekunden `GET /api/laeufe/[id]/zustand` — zwei Techniker
teilen sich ein Onboarding und sollen die Arbeit des anderen sehen, ohne die Seite neu zu laden.
Diese Antwort trägt **nur** Status und Schrittzustände, ausdrücklich keine fertigen Befehle.
Der Grund ist `{{generiertes_passwort}}`: es entsteht bei jedem Auflösen neu. Lieferte das Polling
Befehle mit, stünde alle zehn Sekunden ein anderes Passwort auf dem Bildschirm als das, das der
Techniker gerade in die Konsole geklebt hat. Die Befehle entstehen deshalb genau einmal je
Seitenaufbau, und der Client lädt die Seite erst neu, wenn sich am Zustand wirklich etwas geändert
hat. Bei verstecktem Tab pausiert die Abfrage.
Der Route Handler prüft sein Recht selbst und gibt einen fremden Lauf als „nicht gefunden“ zurück.
Die Hülle unter `src/app/(app)/layout.tsx` deckt `src/app/api/` nicht ab.
## 6. Betrieb: zwei Wege, eine Regel
Die Datenbank ist auf beiden Wegen von außen nicht erreichbar: