74 lines
5.5 KiB
Markdown
74 lines
5.5 KiB
Markdown
# #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.
|