docs(issues): Phase 3 schneiden, und Verweise zeigen wieder richtig
Vier Meilensteine fuer v0.3.0, 34 h geschaetzt: bedingte Schritte und Abschnitte (M23), Vier-Augen-Freigabe (M24), PDF-Uebergabeprotokoll (M25), weitere Schritt-Typen (M26). Die Reihenfolge ist begruendet: M23 zuerst, weil es als einziges am Datenmodell und an der Abschlussregel arbeitet; M26 zuletzt, weil UNTERSCHRIFT erst im PDF einen Zweck hat. Jedes Issue nennt die Entscheidungen, die zu treffen sind, mit Empfehlung und Begruendung. Bei M23 ist die entscheidende: eine Bedingung auf einer Variable liesse sich beim Start festschreiben wie bei #031, eine auf einem Schrittergebnis nicht — die Antwort entsteht erst waehrend der Arbeit. Beim Nummernvergeben einen Fund: vier Codestellen, sechs Changelog-Zeilen und der Doku-Pruefer nannten dreistellig die Tracker-Nummern 39 und 40 — gemeint waren die Issue-Dateien 025 und 026. Solange es diese Dateien nicht gab, fiel es niemandem auf; mit Phase 3 waere es eine falsche Spur geworden, und zwar in Kommentaren, die eine Codestelle begruenden. Behoben, und pruefe:doku prueft es ab jetzt: jeder dreistellige Verweis muss auf eine Issue-Datei zeigen. Gegenprobe mit einer erfundenen Nummer schlaegt an. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
60aaf746cf
commit
2f247b3617
+6
-6
@@ -44,15 +44,15 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
|
||||
- Der Healthcheck `/api/gesundheit` nennt im Fehlerfall keinen Grund mehr. Der Pfad ist ohne
|
||||
Anmeldung erreichbar, und eine Meldung von Prisma oder `pg` nennt Host, Port, Benutzernamen und
|
||||
Datenbanknamen. Nach außen steht jetzt nur noch `{"status":"fehler"}` mit Status 503; die
|
||||
Begründung geht ins Protokoll des Prozesses (#039)
|
||||
Begründung geht ins Protokoll des Prozesses (#025)
|
||||
- Die Konvention „nie auf rohe Hexwerte“ nennt ihre sechs begründeten Ausnahmen beim Namen —
|
||||
Druckansichten, QR-Code und `themeColor` entscheiden nicht nach unserem Farbschema. Die Regel war
|
||||
strenger formuliert als gemeint (#040)
|
||||
strenger formuliert als gemeint (#026)
|
||||
- `CLAUDE.md` und `README.md` nennen den LDAP-Testcontainer als Teil des Tors vor dem Commit. Ohne
|
||||
ihn überspringt die Suite **22 Tests** — den ganzen Verzeichnis-Abgleich —, sagt das auf stderr,
|
||||
und der Lauf sieht trotzdem grün aus (#040)
|
||||
und der Lauf sieht trotzdem grün aus (#026)
|
||||
- Im README fehlte `format:pruefen` in den Qualitätsschranken, obwohl `CLAUDE.md` es seit #010
|
||||
verlangt (#040)
|
||||
verlangt (#026)
|
||||
|
||||
### Behoben
|
||||
|
||||
@@ -235,9 +235,9 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
|
||||
eingespielte Kopie bekommt **keinen** Schlüssel: sie soll nicht den Platz des Originals einnehmen
|
||||
(#021)
|
||||
- `pnpm pruefe:doku` vergleicht die Schranken-Befehle in `CLAUDE.md` und `README.md` und verlangt von
|
||||
beiden den LDAP-Container. Genau diese Abweichung war unbemerkt entstanden (#040)
|
||||
beiden den LDAP-Container. Genau diese Abweichung war unbemerkt entstanden (#026)
|
||||
- Fünf Tests für den Healthcheck, darunter einer, der belegt, dass die Begründung ins Protokoll
|
||||
geschrieben und nicht bloß verschluckt wird (#039)
|
||||
geschrieben und nicht bloß verschluckt wird (#025)
|
||||
|
||||
## [0.1.0] – 2026-09-26
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ geprüft. In CI bricht sie stattdessen ab, absichtlich.
|
||||
| `mailpit` | **3 Tests** (nachgezählt: 36 statt 33) | der echte SMTP-Versand in `tests/benachrichtigungen.test.ts` — die vier Anlässe, die Passwort-Prüfung über alle Rumpftexte und „nichts zweimal“ |
|
||||
|
||||
`ldap` fiel bei der Abnahme zu #019 auf, `mailpit` kam mit #034 dazu. Beide Zahlen sind **gemessen**,
|
||||
nicht geschätzt — in #040 hätte beinahe eine geratene in dieser Datei gestanden.
|
||||
nicht geschätzt — in #026 hätte beinahe eine geratene in dieser Datei gestanden.
|
||||
|
||||
`format:pruefen` gehört dazu, seit der Review in #010 darauf gestoßen ist: `lint-staged` formatiert
|
||||
beim Commit automatisch nach, und das Ergebnis wäre ein Commit mit Änderungen, die niemand gesehen
|
||||
|
||||
@@ -11,10 +11,15 @@ Nicht im MVP umsetzen. Hier gesammelt, damit das Datenmodell nichts verbaut.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] PDF-Übergabeprotokoll mit Unterschrift
|
||||
- [ ] Bedingte Schritte und Abschnitte
|
||||
- [ ] Vier-Augen-Freigabe
|
||||
- [ ] Weitere Schritt-Typen
|
||||
Geschnitten am 2026-10-11, nach dem Tag von v0.2.0. Vier Meilensteine, 34 h geschätzt; die
|
||||
Reihenfolge ist nicht beliebig.
|
||||
|
||||
| | Issue | Warum an dieser Stelle |
|
||||
|---|---|---|
|
||||
| M23 | [Bedingte Schritte und Abschnitte](039-bedingte-schritte.md) | zuerst, weil es als einziges am Datenmodell und an der Abschlussregel arbeitet — und weil der echte Fall im Seed steht: „Mitarbeiter-Onboarding“ trägt beide Konto-Abschnitte |
|
||||
| M24 | [Vier-Augen-Freigabe](040-vier-augen-freigabe.md) | danach, weil der Abschluss dann einmal angefasst ist |
|
||||
| M25 | [PDF-Übergabeprotokoll](041-pdf-uebergabeprotokoll.md) | nach M24, damit das Blatt den Freigeber nennen kann |
|
||||
| M26 | [Weitere Schritt-Typen](042-weitere-schritt-typen.md) | zuletzt, weil `UNTERSCHRIFT` erst im PDF einen Zweck hat |
|
||||
|
||||
## Vorleistung im MVP
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# #039 M23 – Bedingte Schritte und Abschnitte
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.3.0
|
||||
**Labels:** phase-3, backend, frontend
|
||||
**Geschätzt:** 12 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
Eine Checkliste enthält heute **alles**, und was nicht zutrifft, wird von Hand auf
|
||||
`NICHT_ZUTREFFEND` gesetzt. Der echte Fall steht im Seed: „Mitarbeiter-Onboarding“ trägt **beide**
|
||||
Konto-Abschnitte — „Konto anlegen (Active Directory)“ mit 5 Schritten und „Konto anlegen (Entra
|
||||
ID)“ mit 4 —, obwohl in jedem einzelnen Lauf nur einer gilt. Die Variable `konto_typ` sagt es
|
||||
bereits; niemand wertet sie aus.
|
||||
|
||||
Neun Schritte, die ein Techniker bei jedem Onboarding wegklickt. Die Abschnitts-Aktion aus #013
|
||||
macht es erträglich, aber sie ist ein Pflaster: der Fortschrittsbalken zählt die Schritte mit, und
|
||||
im Archiv steht später eine Checkliste mit einem Abschnitt, den niemand je gebraucht hat.
|
||||
|
||||
Setzt [#031](031-abteilungsabschnitte.md) voraus: `geltendeAbschnitte` und `nurGeltende` sind die
|
||||
Stelle, an der das sitzt.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
- [ ] **Wann wird ausgewertet?** Das ist die Entscheidung, an der alles hängt.
|
||||
|
||||
Für Abteilungen (#031) wird beim **Start festgeschrieben** — die Abteilung des Laufs steht
|
||||
dann fest, und ein archivierter Lauf muss genau die Schritte zeigen, die damals galten.
|
||||
|
||||
Für Bedingungen reicht das **nicht**: eine Bedingung auf einer Variable (`konto_typ`) ließe
|
||||
sich beim Start auswerten, eine Bedingung auf einem **Schrittergebnis** („wenn Schritt 3 =
|
||||
nein, dann Abschnitt B“) nicht — die Antwort entsteht erst während der Arbeit.
|
||||
|
||||
Empfehlung: **laufend auswerten, Ergebnis mitschreiben.** Die Auswertung läuft bei jeder
|
||||
Änderung an einem Schrittergebnis; was dabei herauskommt, wird in den Lauf geschrieben (wie
|
||||
`geltendeAbschnitte`). Damit gilt für das Archiv weiter „der Lauf weiß, was galt“, und der
|
||||
Ereignisstrom aus #033 trägt die Änderung ohnehin schon zur zweiten Sitzung.
|
||||
- [ ] **Woran darf eine Bedingung hängen?** Empfehlung: an einer **Variable** des Laufs und an
|
||||
einem **Schrittergebnis** derselben Version. Nicht an Gerätetyp, Abteilung oder Rolle — für
|
||||
Abteilungen gibt es #031, und je mehr Quellen, desto schwerer zu erklären, warum ein Schritt
|
||||
fehlt.
|
||||
- [ ] **Wie wird sie gespeichert?** Empfehlung: ein Json-Feld `bedingung` an `VorlagenAbschnitt`
|
||||
und `VorlagenSchritt`, mit einem engen Schema — `{ art: "variable", schluessel, operator,
|
||||
wert }` oder `{ art: "schritt", schrittId, zustand }`. Json, weil `konfig` es schon so macht
|
||||
und ein neuer Operator dann keine Migration braucht.
|
||||
**Eine Bedingung, kein Ausdrucksbaum.** Kein `und`/`oder`, keine Verschachtelung: das wäre
|
||||
eine kleine Sprache, die niemand im Editor pflegen kann, und ein Abschnitt, der aus
|
||||
unerklärlichen Gründen fehlt, ist schlimmer als einer zu viel.
|
||||
- [ ] **Was passiert mit einem Schrittergebnis, das schon Daten trägt**, wenn die Bedingung
|
||||
umschlägt? Empfehlung: die Zeile **bleibt** und wird nur ausgeblendet. Löschen hieße, eine
|
||||
Notiz zu verlieren, die jemand geschrieben hat; und schlägt die Bedingung zurück, ist sie
|
||||
wieder da.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Migration: `bedingung Json?` an `VorlagenAbschnitt` und `VorlagenSchritt`, dazu ein CHECK, der
|
||||
sicherstellt, dass eine Bedingung auf einen Schritt **derselben Version** zeigt (sonst zeigt
|
||||
sie nach dem Veröffentlichen ins Leere)
|
||||
- [ ] `src/lib/bedingungen.ts`, framework-frei: Schema, Auswertung, und die Liste der Schritte und
|
||||
Abschnitte, die gelten. **Dieselbe Stelle für alle drei Schichten**, wie
|
||||
`src/lib/geltende-abschnitte.ts`
|
||||
- [ ] Auswertung bei jedem Schreiben an einem `Schrittergebnis`; das Ergebnis in den Lauf
|
||||
- [ ] Abschlussregel: ein ausgeblendeter Pflichtschritt hält den Abschluss **nicht** auf (dieselbe
|
||||
Regel wie bei #031, dieselbe Funktion)
|
||||
- [ ] Fortschrittsbalken und Zählwerte rechnen nur mit dem, was gilt
|
||||
- [ ] Editor: Bedingung an Abschnitt und Schritt setzen, mit Auswahl der Variable bzw. des Schritts
|
||||
**aus derselben Version**; eine Vorschau, die sagt „gilt, wenn …“
|
||||
- [ ] Warnung im Editor bei einer Bedingung auf einen Schritt, der **nach** dem bedingten steht —
|
||||
sie wäre beim Abarbeiten noch offen, und der Abschnitt erschiene erst rückwirkend
|
||||
- [ ] Abarbeitung: ausgeblendete Abschnitte verschwinden, ohne die Nummerierung zu verschieben
|
||||
(`01 · Vorbereitung` kommt aus der Position und wird nie gespeichert)
|
||||
- [ ] Druckansicht und Archiv zeigen, was galt
|
||||
- [ ] Export/Import: die Bedingung wandert mit, und ein Verweis auf einen Schritt wird dabei auf die
|
||||
**neue** Kennung umgeschrieben — sonst zeigt sie in der zweiten Installation ins Leere
|
||||
- [ ] Seed: „Mitarbeiter-Onboarding“ bekommt die Bedingung auf `konto_typ`, die beiden
|
||||
Konto-Abschnitte bleiben drin und schließen sich gegenseitig aus. Das ist die Gegenprobe am
|
||||
echten Fall.
|
||||
- [ ] Anleitung Kapitel 04 (Vorlagen): wann eine Bedingung das richtige Mittel ist und wann eine
|
||||
zweite Vorlage
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: ein Lauf mit `konto_typ = AD` hat die 5 AD-Schritte und **keinen** der 4 Entra-Schritte
|
||||
- [ ] Test: dasselbe umgekehrt, und bei nicht gesetzter Variable gelten **beide** — eine Bedingung,
|
||||
die nicht entschieden werden kann, blendet nichts aus
|
||||
- [ ] Test: eine Bedingung auf einem Schrittergebnis schlägt um, **während** der Lauf läuft, und der
|
||||
Abschnitt erscheint
|
||||
- [ ] Test: ein ausgeblendeter Pflichtschritt hält den Abschluss nicht auf
|
||||
- [ ] Test: ein Schrittergebnis mit Notiz bleibt stehen, wenn seine Bedingung umschlägt, und ist
|
||||
danach wieder da
|
||||
- [ ] Test: Export und Import einer Vorlage mit Bedingungen ergeben dieselbe Struktur, und der
|
||||
Schritt-Verweis zeigt auf den richtigen Schritt der **neuen** Version
|
||||
- [ ] Test: ein archivierter Lauf zeigt genau die Schritte, die bei seinem Abschluss galten
|
||||
- [ ] Am Handy kein horizontales Scrollen
|
||||
@@ -0,0 +1,68 @@
|
||||
# #040 M24 – Vier-Augen-Freigabe
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.3.0
|
||||
**Labels:** phase-3, backend, frontend
|
||||
**Geschätzt:** 8 h **Tatsächlich:** –
|
||||
|
||||
## 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
|
||||
|
||||
- [ ] **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.
|
||||
- [ ] **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.
|
||||
- [ ] **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.
|
||||
- [ ] **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
|
||||
|
||||
- [ ] Migration: `freigabePflicht Boolean` an `VorlagenVersion`, dazu `freigegebenVonId`,
|
||||
`freigegebenAm` und `freigabeNotiz` am `Lauf`; CHECK, dass `freigegebenVonId` und
|
||||
`freigegebenAm` zusammen gesetzt sind (wie bei `abgeschlossenAm`)
|
||||
- [ ] CHECK: `freigegebenVonId` ist **nicht** `erstelltVonId` — die Regel gehört in die Datenbank,
|
||||
nicht nur in den Dienst (CLAUDE.md)
|
||||
- [ ] Recht `freigeben` in `RECHTE`, Rollenmatrix-Test erweitern
|
||||
- [ ] Dienst: `laufFreigeben(laufId, benutzer, notiz?)` und `laufZurueckweisen(laufId, benutzer,
|
||||
notiz)`; Abschluss verlangt die Freigabe, wenn die Version sie fordert
|
||||
- [ ] Die Prüfung „wer hat daran gearbeitet“ an **einer** Stelle, wie `laufMussVeraenderbarSein`
|
||||
- [ ] Benachrichtigung: ein neuer `Meldeanlass` `LAUF_FREIGABE_OFFEN` an die Rollen mit dem Recht —
|
||||
sonst wartet ein Lauf, von dem niemand weiß
|
||||
- [ ] 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“
|
||||
- [ ] Editor: Häkchen „dieser Lauf braucht eine Freigabe“ an der Version
|
||||
- [ ] Druckansicht und PDF nennen Freigeber und Zeitpunkt
|
||||
- [ ] Anleitung: Kapitel 02 (Läufe abarbeiten) und 04 (Vorlagen), mit dem Hinweis, dass ein
|
||||
Einzelkämpfer sich nicht selbst freigeben kann
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: ein Lauf mit Freigabepflicht lässt sich nicht abschließen, solange keine Freigabe steht
|
||||
- [ ] Test: der Bearbeiter kann **nicht** freigeben — auch nicht als `ADMIN`
|
||||
- [ ] Test: die Datenbank weist es ebenfalls ab (CHECK), nicht nur der Dienst
|
||||
- [ ] Test: eine Rolle ohne `freigeben` wird abgewiesen, über jede Server Action
|
||||
- [ ] Test: Zurückweisen verlangt eine Notiz und setzt den Lauf auf `IN_ARBEIT`
|
||||
- [ ] Test: die Meldung geht an die Rollen mit dem Recht und nicht an alle
|
||||
- [ ] Test: ein Lauf **ohne** Freigabepflicht verhält sich unverändert — bestehende Läufe und
|
||||
Vorlagen merken nichts
|
||||
- [ ] Am Handy kein horizontales Scrollen
|
||||
@@ -0,0 +1,68 @@
|
||||
# #041 M25 – PDF-Übergabeprotokoll mit Unterschrift
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.3.0
|
||||
**Labels:** phase-3, backend, frontend
|
||||
**Geschätzt:** 8 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
Wer ein Notebook übergibt, braucht ein Blatt, das der Empfänger unterschreibt — und das danach
|
||||
auffindbar ist. Heute gibt es die Druckansicht eines Laufs (`/drucken/lauf/[id]`, #015): sie ist
|
||||
hell, vollständig und als Vorstufe genau dafür gebaut. Was fehlt, ist die Datei.
|
||||
|
||||
Ein Browser-Ausdruck genügt nicht: er hängt am Drucker-Dialog des Betrachters, trägt dessen
|
||||
Kopfzeilen und lässt sich nicht an ein Ticket anhängen.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
- [ ] **Wie entsteht das PDF?** Empfehlung: **Chromium aus `@playwright/test`**, wie
|
||||
`scripts/anleitung-pdf.ts` es für die Anleitung tut. Keine neue Abhängigkeit, keine zweite
|
||||
Auslegung des Layouts — gedruckt wird dieselbe Seite, die die Druckansicht zeigt.
|
||||
**Grenze, die genannt gehört:** damit braucht der Server einen Chromium. Im Compose-Stand ist
|
||||
das eine Ergänzung am Image, nativ ein Paket. Die Alternative (`pdf-lib` oder ein
|
||||
Dienst) wäre ein zweites Layout, das auseinanderläuft — der Preis ist höher.
|
||||
- [ ] **Unterschrift auf Papier oder auf dem Gerät?** Empfehlung: **beides, in dieser Reihenfolge.**
|
||||
Zuerst ein Unterschriftsfeld im PDF (eine Linie, Name, Datum) — das löst den Fall heute und
|
||||
kostet fast nichts. Eine Unterschrift mit dem Finger auf dem Handy ist ein eigener Schritt-Typ
|
||||
und gehört zu [#042](042-weitere-schritt-typen.md); sie landet dann als Bild im PDF.
|
||||
Eine digitale **Signatur** im kryptografischen Sinn ist ausdrücklich **nicht** gemeint und
|
||||
wäre eine eigene Phase.
|
||||
- [ ] **Wird das PDF gespeichert oder bei jedem Abruf erzeugt?** Empfehlung: **bei jedem Abruf
|
||||
erzeugt**, nicht gespeichert. Ein gespeichertes PDF ist eine zweite Wahrheit neben dem Lauf;
|
||||
wird eine Notiz später berichtigt, stimmt es nicht mehr. Für den Fall „wir brauchen das Blatt
|
||||
von damals“ gibt es das Archiv, das den Lauf unveränderlich hält.
|
||||
**`{{generiertes_passwort}}` erscheint auch hier nicht** — die Druckansicht zeigt
|
||||
`[Passwort – nicht gespeichert]`, und das PDF übernimmt das.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] `GET /api/laeufe/[id]/protokoll.pdf` — Route Handler, prüft sein Recht **selbst**
|
||||
(`tests/zugang.test.ts` erzwingt das seit #038)
|
||||
- [ ] Erzeugung über Chromium, gegen die eigene Druckansicht; eine Seitenvorlage mit Kopf (Kunde,
|
||||
Lauf, Vorlage, Version), Inhalt und Fuß (Bearbeiter, Freigeber aus
|
||||
[#040](040-vier-augen-freigabe.md), Unterschriftsfeld)
|
||||
- [ ] Ein **Chromium-Vorrat** im Prozess: ihn je Anfrage zu starten kostet Sekunden. Eine Instanz,
|
||||
die nach Leerlauf schließt — und eine Obergrenze gleichzeitiger Erzeugungen, wie
|
||||
`SSE_MAX_STROEME` in #033
|
||||
- [ ] Knopf „Protokoll als PDF“ an Lauf und Archiv
|
||||
- [ ] `Dockerfile` und `deploy/install.sh` um Chromium ergänzen, **mit gemessener Bildgröße** im
|
||||
Changelog — eine Ergänzung am Image, die niemand bemerkt, ist eine böse Überraschung beim
|
||||
nächsten Rollout
|
||||
- [ ] `docs/betrieb/` sagt, was die Erzeugung braucht und was passiert, wenn Chromium fehlt:
|
||||
**eine Meldung, kein stilles leeres PDF**
|
||||
- [ ] Anleitung Kapitel 02 und 10 (Archiv)
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: das PDF enthält Titel, Vorlage mit Version, alle geltenden Schritte mit Zustand, Notizen
|
||||
und Kommentare — geprüft am extrahierten Text, nicht am Vorhandensein der Datei
|
||||
- [ ] Test: es enthält **kein** erzeugtes Passwort, über alle Schritt-Typen
|
||||
- [ ] Test: ein Lauf eines fremden Kunden ist über die Adresse nicht abrufbar (wie #032 nach #036)
|
||||
- [ ] Test: ohne Recht `401`, mit falschem Kunden `404`
|
||||
- [ ] Test: fehlt Chromium, kommt eine Meldung mit Hinweis auf die Betriebsdoku — kein leeres PDF
|
||||
- [ ] Test: zwei gleichzeitige Erzeugungen laufen, die dritte wartet oder wird abgewiesen (die
|
||||
Obergrenze gilt)
|
||||
- [ ] Ein Blatt ist ausgedruckt und gegengesehen worden — das ist eine Abnahme von Hand und steht
|
||||
als solche im Devlog
|
||||
@@ -0,0 +1,62 @@
|
||||
# #042 M26 – Weitere Schritt-Typen
|
||||
|
||||
**Status:** offen
|
||||
**Im Tracker:** –
|
||||
**Meilenstein:** v0.3.0
|
||||
**Labels:** phase-3, backend, frontend
|
||||
**Geschätzt:** 6 h **Tatsächlich:** –
|
||||
|
||||
## Ziel
|
||||
|
||||
`SchrittTyp` hat heute sechs Werte: `HAKEN TEXT ZAHL AUSWAHL DATUM BEFEHL`. Das Enum und das
|
||||
Json-Feld `konfig` sind seit #006 genau dafür gebaut, dass neue Typen ohne Migration der
|
||||
bestehenden dazukommen.
|
||||
|
||||
Die Frage ist nicht, **wie** ein Typ dazukommt, sondern **welche** — und das Issue nennt sie
|
||||
absichtlich noch nicht als beschlossen: ein Typ, der im Editor steht und in keiner Vorlage
|
||||
auftaucht, ist ungenutzter und damit ungeprüfter Code.
|
||||
|
||||
## Zu entscheiden
|
||||
|
||||
- [ ] **Welche Typen?** Vorschlag, nach Nutzen geordnet, jeder mit dem Fall, der ihn verlangt:
|
||||
|
||||
| Typ | Der Fall | Ohne ihn |
|
||||
|---|---|---|
|
||||
| `DATEI` | „Foto des Typenschilds“ als **Pflicht** | ein Anhang ist seit #032 möglich, aber nie verlangt — ein Lauf schließt ohne ihn ab |
|
||||
| `UNTERSCHRIFT` | Übergabe mit dem Finger auf dem Handy | das Blatt muss gedruckt und wieder eingescannt werden ([#041](041-pdf-uebergabeprotokoll.md)) |
|
||||
| `MEHRFACHAUSWAHL` | „welche Lizenzen zugewiesen“ | heute ein Textfeld, also nicht auswertbar |
|
||||
| `URL` | „Link zum Ticket“ | ein Textfeld ohne Prüfung und ohne Klick |
|
||||
| `UEBERSCHRIFT` | eine Zwischenüberschrift ohne Zustand | ein `HAKEN`, den jemand abhaken muss, damit der Lauf fertig wird |
|
||||
|
||||
**Empfehlung: `DATEI` und `UNTERSCHRIFT`**, und die drei anderen nur, wenn eine Seed-Vorlage
|
||||
sie wirklich benutzt. `UEBERSCHRIFT` ist der zweifelhafteste: ein Schritt ohne Zustand bricht
|
||||
die Annahme „jeder Schritt hat ein `Schrittergebnis`“, und die steckt an vielen Stellen.
|
||||
- [ ] **Bleibt `Schrittergebnis.wert` ein Json?** Ja — es ist seit #006 eines, genau dafür. Eine
|
||||
Mehrfachauswahl ist ein Array, eine Unterschrift ein Verweis auf einen `Anhang`.
|
||||
- [ ] **Wie verträgt sich `DATEI` mit dem Archiv-Schreibschutz?** Ein Pflicht-Anhang muss vor dem
|
||||
Abschluss da sein; danach kommt keiner mehr weg (#032). Das ist bereits so und braucht nur
|
||||
einen Test.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Je gewählten Typ: Enum-Wert, Zod-Schema für `konfig` und `wert`, Darstellung im Editor,
|
||||
Darstellung in der Abarbeitung, Darstellung im Druck und im PDF
|
||||
- [ ] Die Abschlussregel kennt den neuen Typ: was zählt als „erledigt“? Bei `DATEI` ein Anhang, bei
|
||||
`UNTERSCHRIFT` ein Bild, bei `MEHRFACHAUSWAHL` mindestens eine Wahl
|
||||
- [ ] `UNTERSCHRIFT`: Zeichenfläche am Handy (Pointer-Ereignisse, nicht Touch-spezifisch), Ergebnis
|
||||
als PNG über den Anhänge-Weg aus #032 — **keine neue Ablage**
|
||||
- [ ] Export/Import und die Vorlagen-Austauschform um die neuen Typen erweitern, nachsichtig: eine
|
||||
Datei mit einem unbekannten Typ wird abgewiesen **mit Nennung des Typs**, nicht stillschweigend
|
||||
verändert
|
||||
- [ ] Mindestens eine Seed-Vorlage benutzt jeden neuen Typ — sonst ist er ungeprüfter Code
|
||||
- [ ] Anleitung Kapitel 04, mit dem Satz, wann ein Typ das richtige Mittel ist
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test je Typ: anlegen, abarbeiten, abschließen, drucken
|
||||
- [ ] Test: ein `DATEI`-Pflichtschritt ohne Anhang hält den Abschluss auf
|
||||
- [ ] Test: eine Unterschrift landet als `Anhang` und nicht als Json-Blob in `wert`
|
||||
- [ ] Test: ein Lauf einer **älteren** Version verhält sich unverändert — die neuen Typen tauchen
|
||||
dort nicht auf, und keine Migration hat Daten angefasst
|
||||
- [ ] Test: eine Austauschdatei mit unbekanntem Typ wird abgewiesen und nennt den Typ
|
||||
- [ ] Am Handy bedienbar, bei 390 px geprüft — besonders die Zeichenfläche
|
||||
@@ -43,4 +43,8 @@ Diese Dateien sind die Quelle; `pnpm issues:sync` spiegelt sie in den Gitea-Trac
|
||||
| #036 | [Reviewbefunde aus der Durchsicht von Phase 2](036-reviewbefunde-phase-2.md) | erledigt | v0.2.0 | 4 h | 3 h 40 min |
|
||||
| #037 | [Zwei Nachzügler aus der Durchsicht von Phase 2](037-anhaenge-lesemodell.md) | erledigt | v0.2.0 | 2 h | 1 h 50 min |
|
||||
| #038 | [Die E2E-Abläufe laufen in keinem Tor und waren monatelang rot](038-e2e-im-tor.md) | erledigt | v0.2.0 | 3 h | 1 h 35 min |
|
||||
| | **Summe** | | | **238 h** | **73 h 55 min** |
|
||||
| #039 | [M23 – Bedingte Schritte und Abschnitte](039-bedingte-schritte.md) | offen | v0.3.0 | 12 h | – |
|
||||
| #040 | [M24 – Vier-Augen-Freigabe](040-vier-augen-freigabe.md) | offen | v0.3.0 | 8 h | – |
|
||||
| #041 | [M25 – PDF-Übergabeprotokoll mit Unterschrift](041-pdf-uebergabeprotokoll.md) | offen | v0.3.0 | 8 h | – |
|
||||
| #042 | [M26 – Weitere Schritt-Typen](042-weitere-schritt-typen.md) | offen | v0.3.0 | 6 h | – |
|
||||
| | **Summe** | | | **272 h** | **73 h 55 min** |
|
||||
|
||||
+55
-1
@@ -141,7 +141,7 @@ function feld(text, name) {
|
||||
// README fehlte `format:pruefen`, und beide nannten den LDAP-Container nicht, ohne
|
||||
// den die Suite 22 Tests still ueberspringt. Beides fiel erst bei der Abnahme zu
|
||||
// #019 auf -- ein gruener Lauf sieht genauso aus. Geprueft wird deshalb der
|
||||
// Befehlsblock selbst, nicht seine Umgebung (#040).
|
||||
// Befehlsblock selbst, nicht seine Umgebung (#026).
|
||||
{
|
||||
/** Der erste eingezaeunte Block nach einer Ueberschrift. */
|
||||
const blockNach = (text, ueberschrift) => {
|
||||
@@ -197,6 +197,60 @@ function feld(text, name) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 7. Jeder (#NNN)-Verweis zeigt auf eine Issue-Datei --------------------
|
||||
{
|
||||
/*
|
||||
* Verweise wie `(#032)` stehen in Kommentaren, im Changelog und in CLAUDE.md
|
||||
* und sind die einzige Bruecke zwischen einer Codestelle und ihrer
|
||||
* Begruendung. Zeigt einer ins Leere, ist die Begruendung verloren — und
|
||||
* schlimmer: zeigt er auf die **falsche** Datei, fuehrt er jemanden in die
|
||||
* Irre, der ihm vertraut.
|
||||
*
|
||||
* Genau das war passiert: vier Codestellen und sechs Changelog-Zeilen nannten
|
||||
* dreistellig die **Tracker**-Nummern 39 und 40 — gemeint waren die
|
||||
* Issue-Dateien 025 und 026. Solange es keine Dateien mit diesen Nummern gab,
|
||||
* fiel es niemandem auf; mit Phase 3 waere es eine falsche Spur geworden.
|
||||
* Verbindlich ist die **Dateinummer**; die Tracker-Nummer steht in der
|
||||
* Issue-Datei unter „Im Tracker“.
|
||||
*
|
||||
* Diese Datei nennt deshalb selbst keine Nummer in der dreistelligen Form —
|
||||
* sonst meldete die Pruefung ihren eigenen Kommentar.
|
||||
*/
|
||||
const verz = "docs/issues";
|
||||
const nummern = new Set(
|
||||
readdirSync(join(wurzel, verz))
|
||||
.filter((n) => /^\d{3}-.+\.md$/.test(n))
|
||||
.map((n) => n.slice(0, 3)),
|
||||
);
|
||||
|
||||
const sammeln = (ordner) =>
|
||||
readdirSync(ordner, { withFileTypes: true }).flatMap((eintrag) => {
|
||||
if (["node_modules", ".git", ".next", "generated", "test-results"].includes(eintrag.name)) {
|
||||
return [];
|
||||
}
|
||||
const pfad = join(ordner, eintrag.name);
|
||||
if (eintrag.isDirectory()) return sammeln(pfad);
|
||||
return /\.(ts|tsx|mjs|md|sql)$/.test(eintrag.name) ? [pfad] : [];
|
||||
});
|
||||
|
||||
for (const datei of sammeln(wurzel)) {
|
||||
const kurz = datei.replace(`${wurzel}/`, "");
|
||||
// Der Devlog zitiert Tracker-Nummern aus Pull Requests; dort gilt die Regel nicht.
|
||||
if (kurz === "docs/devlog.md") continue;
|
||||
const text = readFileSync(datei, "utf8");
|
||||
const gemeldet = new Set();
|
||||
for (const treffer of text.matchAll(/#(\d{3})\b/g)) {
|
||||
const nummer = treffer[1];
|
||||
if (nummern.has(nummer) || gemeldet.has(nummer)) continue;
|
||||
gemeldet.add(nummer);
|
||||
melde(
|
||||
`${kurz} verweist auf #${nummer}, aber ${verz}/${nummer}-*.md gibt es nicht. ` +
|
||||
`Gemeint ist vielleicht eine Tracker-Nummer — verbindlich ist die Dateinummer.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Ergebnis --------------------------------------------------------------
|
||||
if (befunde.length > 0) {
|
||||
console.error(`\nDokumentation und Code laufen auseinander (${befunde.length}):\n`);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { prisma } from "@/lib/db";
|
||||
* Datenbanknamen. Nach aussen genuegt, **ob** es geht; warum nicht, steht im
|
||||
* Protokoll des Prozesses (`docker compose logs web`,
|
||||
* `journalctl -u laufzettel`). Dasselbe Muster wie in
|
||||
* `src/server/actions/verzeichnis.ts` (#039).
|
||||
* `src/server/actions/verzeichnis.ts` (#025).
|
||||
*/
|
||||
export async function GET(): Promise<Response> {
|
||||
try {
|
||||
|
||||
@@ -149,7 +149,7 @@ export async function tokenHolen(zugang: GraphZugang, jetzt = Date.now()): Promi
|
||||
/*
|
||||
* Die ausführliche Meldung ins Protokoll des Prozesses, eine kurze nach
|
||||
* draußen. Dieselbe Trennung wie beim LDAP-Bind-Fehler (#016) und beim
|
||||
* Healthcheck (#039): eine Meldung, die Mandant und Anwendungs-ID nennt,
|
||||
* Healthcheck (#025): eine Meldung, die Mandant und Anwendungs-ID nennt,
|
||||
* gehört nicht in eine Oberfläche.
|
||||
*/
|
||||
console.error(
|
||||
|
||||
@@ -46,7 +46,7 @@ export function alsScimAntwort(fehler: unknown): Response {
|
||||
/*
|
||||
* Alles andere ist ein Fehler bei uns, nicht beim Anbieter. Die Meldung geht
|
||||
* ins Protokoll des Prozesses und **nicht** in die Antwort: eine
|
||||
* Datenbankmeldung nennt Host, Port und Benutzernamen (#039).
|
||||
* Datenbankmeldung nennt Host, Port und Benutzernamen (#025).
|
||||
*/
|
||||
console.error(
|
||||
"SCIM-Anfrage fehlgeschlagen:",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
/**
|
||||
* Der Healthcheck (#039).
|
||||
* Der Healthcheck (#025).
|
||||
*
|
||||
* Dieser Pfad ist der einzige, der ohne Anmeldung **und** mit Datenbankzugriff
|
||||
* antwortet. Geprueft wird deshalb nicht, dass er funktioniert — das tun der
|
||||
|
||||
Reference in New Issue
Block a user