Files
Laufzettel/docs/issues/009-vorlagen-versionierung.md
T
florian.wilde d8d5444e9f
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(vorlagen): Versionierung, Unveraenderlichkeit und JSON-Austausch (#23)
2026-09-25 06:10:01 +00:00

74 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# #009 M4 – Vorlagen-Versionierung im Service
**Status:** erledigt
**Meilenstein:** v0.1.0
**Labels:** phase-1, backend
**Geschätzt:** 6 h **Tatsächlich:** 1 h 20 min
## Ziel
Die harte Regel der Anwendung: eine veröffentlichte Version ist für immer unveränderlich, damit ein Archiv-Lauf für immer nachvollziehbar bleibt. Diese Regel sitzt in der Service-Schicht, nicht in der Oberfläche.
## Aufgaben
- [x] Services für `Vorlage`, `VorlagenVersion`, `VorlagenAbschnitt`, `VorlagenSchritt`, `VorlagenVariable`
- [x] `versionVeroeffentlichen`: setzt `status = VEROEFFENTLICHT`, `veroeffentlichtAm`, `veroeffentlichtVonId`, Änderungsnotiz, `Protokoll`-Eintrag
- [x] `entwurfHolenOderErzeugen`: tiefe Kopie der letzten veröffentlichten Version als neuer Entwurf mit fortlaufender `version`; kopiert Variablen, Abschnitte und Schritte samt `konfig`
- [x] Höchstens ein Entwurf je Vorlage — durchgesetzt vom partiellen Eindeutigkeitsindex `VorlagenVersion_ein_entwurf`, der zweite gleichzeitige Aufruf bekommt denselben Entwurf
- [x] Jede Schreiboperation auf einer veröffentlichten Version wirft; zentral in `versionMussEntwurfSein()` — **und** zusätzlich in der Datenbank per Trigger
- [x] Läufe verweisen ausschließlich auf veröffentlichte Versionen — `veroeffentlichteVersionPruefen()` für #013
- [x] `vorlageDuplizieren`: neue Vorlage mit Entwurf als Kopie
- [x] JSON-Export und -Import mit Zod-Schema, Format in `docs/vorlagen-format.md` dokumentiert (Version des Formats, Abschnitte, Schritte, Variablen, `konfig`)
- [x] Rechte: `REDAKTEUR` und `ADMIN` dürfen anlegen, bearbeiten, veröffentlichen; `TECHNIKER` und `LESER` nicht
## Akzeptanzkriterien
- [x] Test: nach dem Veröffentlichen schlägt jede Änderung an Version, Abschnitten, Schritten und Variablen fehl
- [x] Test: „Bearbeiten“ erzeugt eine tiefe Kopie — Änderungen am Entwurf lassen die veröffentlichte Version unberührt, auch in verschachtelten `konfig`-Feldern
- [x] Test: ein zweiter Entwurf wird abgelehnt
- [x] Test: ein Lauf auf Version 1 bleibt nach Veröffentlichen von Version 2 unverändert und zeigt weiter die Schritte aus Version 1
- [x] Test: Export und anschließender Import ergeben eine inhaltsgleiche Vorlage
- [x] Test: `TECHNIKER` darf nicht veröffentlichen
- [x] Test: Veröffentlichen schreibt einen `Protokoll`-Eintrag
## Beim Bauen aufgefallen
- **Die Lesemodelle haben die Mandantengrenze verloren, sobald jemand suchte.** Der Suchfilter
brachte einen zweiten `OR`-Schlüssel in dasselbe Objekt; in JavaScript gewinnt der letzte, also
ersetzte er die Kundenbedingung. Ein einzelner Buchstabe im Suchfeld gab einem gebundenen
Techniker alle Vorlagen aller Kunden. `versionsHistorie` und `versionMitAufbau` haben überhaupt
nicht gefiltert — damit lagen Abschnittstitel, Schritte und PowerShell-Befehle fremder Kunden
offen. Ursache ist banal: zu `src/server/queries/vorlagen.ts` gab es keinen einzigen Test. Es
gibt jetzt `tests/vorlagen-sicht.test.ts`, und die Kundenbedingung steht in einem `AND`-Eintrag,
wo kein weiterer Filter sie überschreiben kann.
- **Die Unveränderlichkeit hing allein am Service.** Migrationen, `psql`, ein Seed-Skript und ab
Phase 4 die Connectoren schreiben daran vorbei — und genau diese Regel ist der Grund, warum ein
Archiv-Lauf in zehn Jahren noch etwas wert ist. Vier Trigger setzen sie jetzt in der Datenbank
durch, einer davon verbietet den Rückweg `VEROEFFENTLICHT → ENTWURF`, der sonst alles andere
ausgehebelt hätte.
- **Prisma will handgeschriebene Datenbankobjekte löschen.** `prisma migrate dev` erzeugte für die
neue Migration sieben `DROP`-Anweisungen gegen Trigger, zusammengesetzte Fremdschlüssel und
Trigramm-Indizes — aus Prismas Sicht sind sie Abweichungen von der Vorgabe. Wer sie einmal
übersieht, entfernt stillschweigend die Mandantengrenze. `tests/datenbankregeln.test.ts` zählt
diese Objekte jetzt namentlich auf und macht jeden Verlust laut.
- **Die Trigger haben schlechte Test-Fixtures entlarvt.** Mehrere legten eine Version als
`VEROEFFENTLICHT` an und füllten sie *danach* — also genau das, was die Regel verbietet. Der
Service hatte das nie gesehen, weil die Fixtures direkt über Prisma schrieben.
- **Zwei Redakteure am selben Entwurf überschrieben sich spurlos.** Entwürfe werden bewusst nicht
protokolliert, die Arbeit wäre also auch nicht wiederherstellbar gewesen. `entwurfSpeichern`
nimmt jetzt den erwarteten Stand (`geaendertAm`) und weist ein Speichern auf einem überholten
Stand ab.
- **`Zod` war achtmal einzeln eingebunden**, die deutschen Meldungen hingen davon ab, dass jede
Datei daran denkt. Jetzt gibt es `src/lib/validierung.ts` als einzige Quelle, und eine
ESLint-Regel weist einen direkten `zod`-Import ab.
- **Der Doku-Link eines Schritts nahm jedes Schema an**, auch `javascript:alert(1)`. Er landet in
der Abarbeitungsansicht als `href` — über eine importierte Datei wäre das fremder Code im
Browser des Technikers. Erlaubt sind jetzt nur `http` und `https`.
- **Der Export verwarf bei jeder Beanstandung das ganze `konfig`.** Ein `shell: "zsh"` aus einer
späteren Fassung kostete lautlos auch den `befehl` — in genau der Datei, die jemand zum Umzug
braucht. Exportiert wird jetzt der rohe Inhalt; abgewiesen wird beim Import, laut und mit Pfad.
- **Zwei gleichzeitige Testläufe gegen dieselbe Datenbank** (mein Lauf und der des Review-Agenten)
ergaben 62 falsche Fehlschläge, weil `TRUNCATE` dem anderen die Daten unter den Füßen wegzieht.
Das war mein Fehler im Auftrag an den Agenten; `CLAUDE.md` nennt jetzt den Befehl für eine
eigene Review-Datenbank.