Eine veroeffentlichte Vorlagen-Version ist fuer immer unveraenderlich — daran haengt, dass ein archivierter Lauf in zehn Jahren noch genau die Schritte zeigt, mit denen er gestartet wurde. Die Regel steht deshalb zweimal: im Service, der die verstaendliche Meldung gibt, und in der Datenbank, die die Garantie gibt. Vier Trigger fangen Schreibzugriffe ab, die nicht durch den Service kommen — Migrationen, psql, der Seed und ab Phase 4 die Connectoren — und versperren den Rueckweg nach ENTWURF, der alles andere ausgehebelt haette. - Service: anlegen, Kopfdaten bearbeiten, Entwurf als tiefe Kopie der letzten veroeffentlichten Version, speichern, veroeffentlichen, verwerfen, duplizieren, archivieren - Hoechstens ein Entwurf je Vorlage, durchgesetzt vom partiellen Index; zwei gleichzeitige Klicks auf „Bearbeiten“ bekommen denselben Entwurf statt eines Fehlers - Optimistisches Sperren ueber geaendertAm: wer auf einem ueberholten Stand speichert, wird abgewiesen statt die Arbeit des anderen spurlos zu ersetzen - JSON-Export und -Import samt Format-Dokumentation; der Export ist nachsichtig, der Import streng - src/lib/validierung.ts als einzige Zod-Quelle mit deutschen Meldungen, per ESLint-Regel erzwungen Der unabhaengige Review fand drei Blocker, alle an der Mandantengrenze: der Suchfilter brachte ein zweites OR mit und ueberschrieb die Kundenbedingung, Historie und Aufbau einer Version filterten gar nicht. Zu queries/vorlagen.ts gab es keinen Test — jetzt gibt es tests/vorlagen-sicht.test.ts. 428 Tests gruen (vorher 336). Schliesst #009 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.5 KiB
5.5 KiB
#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
- Services für
Vorlage,VorlagenVersion,VorlagenAbschnitt,VorlagenSchritt,VorlagenVariable versionVeroeffentlichen: setztstatus = VEROEFFENTLICHT,veroeffentlichtAm,veroeffentlichtVonId, Änderungsnotiz,Protokoll-EintragentwurfHolenOderErzeugen: tiefe Kopie der letzten veröffentlichten Version als neuer Entwurf mit fortlaufenderversion; kopiert Variablen, Abschnitte und Schritte samtkonfig- Höchstens ein Entwurf je Vorlage — durchgesetzt vom partiellen Eindeutigkeitsindex
VorlagenVersion_ein_entwurf, der zweite gleichzeitige Aufruf bekommt denselben Entwurf - Jede Schreiboperation auf einer veröffentlichten Version wirft; zentral in
versionMussEntwurfSein()— und zusätzlich in der Datenbank per Trigger - Läufe verweisen ausschließlich auf veröffentlichte Versionen —
veroeffentlichteVersionPruefen()für #013 vorlageDuplizieren: neue Vorlage mit Entwurf als Kopie- JSON-Export und -Import mit Zod-Schema, Format in
docs/vorlagen-format.mddokumentiert (Version des Formats, Abschnitte, Schritte, Variablen,konfig) - Rechte:
REDAKTEURundADMINdürfen anlegen, bearbeiten, veröffentlichen;TECHNIKERundLESERnicht
Akzeptanzkriterien
- Test: nach dem Veröffentlichen schlägt jede Änderung an Version, Abschnitten, Schritten und Variablen fehl
- Test: „Bearbeiten“ erzeugt eine tiefe Kopie — Änderungen am Entwurf lassen die veröffentlichte Version unberührt, auch in verschachtelten
konfig-Feldern - Test: ein zweiter Entwurf wird abgelehnt
- Test: ein Lauf auf Version 1 bleibt nach Veröffentlichen von Version 2 unverändert und zeigt weiter die Schritte aus Version 1
- Test: Export und anschließender Import ergeben eine inhaltsgleiche Vorlage
- Test:
TECHNIKERdarf nicht veröffentlichen - 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.versionsHistorieundversionMitAufbauhaben überhaupt nicht gefiltert — damit lagen Abschnittstitel, Schritte und PowerShell-Befehle fremder Kunden offen. Ursache ist banal: zusrc/server/queries/vorlagen.tsgab es keinen einzigen Test. Es gibt jetzttests/vorlagen-sicht.test.ts, und die Kundenbedingung steht in einemAND-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ückwegVEROEFFENTLICHT → ENTWURF, der sonst alles andere ausgehebelt hätte. - Prisma will handgeschriebene Datenbankobjekte löschen.
prisma migrate deverzeugte für die neue Migration siebenDROP-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.tszählt diese Objekte jetzt namentlich auf und macht jeden Verlust laut. - Die Trigger haben schlechte Test-Fixtures entlarvt. Mehrere legten eine Version als
VEROEFFENTLICHTan 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.
entwurfSpeichernnimmt jetzt den erwarteten Stand (geaendertAm) und weist ein Speichern auf einem überholten Stand ab. Zodwar achtmal einzeln eingebunden, die deutschen Meldungen hingen davon ab, dass jede Datei daran denkt. Jetzt gibt essrc/lib/validierung.tsals einzige Quelle, und eine ESLint-Regel weist einen direktenzod-Import ab.- Der Doku-Link eines Schritts nahm jedes Schema an, auch
javascript:alert(1). Er landet in der Abarbeitungsansicht alshref— über eine importierte Datei wäre das fremder Code im Browser des Technikers. Erlaubt sind jetzt nurhttpundhttps. - Der Export verwarf bei jeder Beanstandung das ganze
konfig. Einshell: "zsh"aus einer späteren Fassung kostete lautlos auch denbefehl— 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
TRUNCATEdem anderen die Daten unter den Füßen wegzieht. Das war mein Fehler im Auftrag an den Agenten;CLAUDE.mdnennt jetzt den Befehl für eine eigene Review-Datenbank.