Files
Laufzettel/docs/issues/005-geruest-prozess-design.md
T
Florian WildeandClaude Opus 5 abacad1cb7
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
feat(vorlagen): Versionierung, Unveraenderlichkeit und JSON-Austausch
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>
2026-09-25 08:09:21 +02:00

5.6 KiB
Raw Blame History

#005 M0 – Gerüst, Prozess, Design-Token

Status: erledigt Meilenstein: v0.1.0 Labels: phase-1, infra, docs Geschätzt: 6 h Tatsächlich: 1 h 25 min

Ziel

Ein lauffähiges, leeres Projekt mit allen Qualitätsschranken, dem gehärteten Betriebsstand und dem Design-Fundament. Danach ist jeder weitere Meilenstein reine Fachlichkeit.

Aufgaben

  • Lokalen Branch master auf main umbenennen (Gitea-Default ist main), Remote unverändert lassen
  • .nvmrc = 24, packageManager: "pnpm@10.34.5" über corepack, engines.node >= 24
  • Next 16.3.6 (App Router, TypeScript strict) mit output: "standalone", src/-Layout
  • tsconfig.json: strict, noUncheckedIndexedAccess, verbatimModuleSyntax, forceConsistentCasingInFileNames, Alias @/* → ./src/*
  • Tailwind 4 (@import "tailwindcss" + @theme, @tailwindcss/postcss), shadcn/ui initialisieren, lucide-react
  • ESLint flat config nach ProxLedger-Muster (eslint-config-next/core-web-vitals + /typescript + eslint-config-prettier/flat, globalIgnores inkl. src/generated/**), Prettier (printWidth: 110, singleQuote: false, trailingComma: "all")
  • husky + lint-staged + commitlint (@commitlint/config-conventional) als Pre-Commit- und Commit-Msg-Hook
  • Vitest 5 (vitest.config.mts, environment: "node", fileParallelism: false) und vite@^7 als Peer; Playwright 1.63 (playwright.config.ts)
  • Dockerfile multi-stage (Basis → Abhängigkeiten → Bauen → Laufzeit), node:24-alpine, tini, Nutzer 1001, non-root
  • docker-compose.yml gehärtet: postgres ohne ports:, Netze intern (internal: true) und extern, web auf ${BIND_ADDR:-127.0.0.1}:3000, migrate als One-Shot aus der Bau-Stufe, Healthchecks, ${VAR:?… fehlt in der .env}
  • docker-compose.dev.yml (nur Postgres, Port 5433) und docker-compose.test.yml (Postgres; der OpenLDAP-Dienst kommt mit #016, wo er tatsächlich benutzt wird)
  • .env.example mit allen Variablen und Kommentar je Variable
  • GET /api/gesundheit → SELECT 1 → 200/503
  • Sicherheitskopfzeilen in next.config.ts, CSP-Nonce in proxy.ts (Next 16: nicht middleware.ts)
  • scripts/pruefe-doku.mjs und pnpm pruefe:doku: Version hat Changelog-Abschnitt, Issue-Dateien und Übersicht stimmen überein, verlinkte Bilder existieren
  • scripts/gitea-issues.ts und pnpm issues:sync: spiegelt docs/issues/*.md in den Gitea-Tracker
  • .gitea/workflows/ci.yml nach ProxLedger-Muster, erweitert um Playwright und shellcheck
  • Design-Token als CSS-Variablen (Grund #0B0D10, Flächen #14171C/#1B1F26, Linien #262B33, Text #F3F5F7/#98A2B3, Akzent #C6F135, Status #3DDC97/#F5B84A/#F87171), heller Modus optional
  • Schriften selbst gehostet: @fontsource-variable/space-grotesk, -manrope, -jetbrains-mono
  • Wortmarke laufzettel (Space Grotesk 700, Laufweite −0.04em, Akzentpunkt) und Bildmarke [ ✓ ] als SVG, Favicon, PWA-Icon, manifest.ts
  • App-Shell: dunkle Sidebar mit „Scannen“-Knopf direkt unter dem Logo, Pillen-Buttons (40 px, am Handy 44–46 px), Karten mit Radius 16–18 px und 1-px-Linie ohne Schatten
  • Versionsanzeige unten links aus NEXT_PUBLIC_APP_VERSION und NEXT_PUBLIC_GIT_SHA
  • README.md, CHANGELOG.md (Keep a Changelog, deutsch), docs/devlog.md, CLAUDE.md mit Konventionen und Namensabbildung, docs/architektur.md, docs/abnahme-mvp.md
  • Gitea: Labels, Milestones v0.1.0…v0.5.0, Epic-Issues #001–#004 und Meilenstein-Issues #005–#019

Akzeptanzkriterien

  • docker compose up startet App und Datenbank, Migrationen laufen automatisch
  • nc -z localhost 5432 schlägt fehl, docker compose exec web nc -z postgres 5432 gelingt
  • Ein Vitest-Test liest docker-compose.yml und schlägt an, wenn unter postgres ein ports: auftaucht
  • pnpm lint && pnpm typecheck && pnpm test && pnpm pruefe:doku grün
  • Ein Commit ohne Conventional-Commits-Betreff wird vom Hook abgelehnt
  • Die App zeigt Wortmarke, Bildmarke und Version unten links im vorgegebenen Farbschema

Beim Bauen aufgefallen

  • pnpm 12 hat die Freigabe von Build-Skripten umbenannt. onlyBuiltDependencies wird von pnpm config list noch angezeigt, aber nicht mehr angewandt; wirksam ist allowBuilds als Zuordnung in pnpm-workspace.yaml. Das pnpm-Feld in package.json liest pnpm 12 gar nicht mehr und warnt darüber.
  • pnpm 12 prüft die Lieferkette. Pakete, die jünger als 24 Stunden sind, werden abgelehnt. vite@8.3.1 und lucide-react@1.48.0 erschienen am selben Tag und mussten auf 8.3.0 bzw. 1.47.0 zurück. Die Richtlinie bleibt an — sie ist sinnvoll.
  • eslint 10 bricht mit eslint-config-next. eslint-plugin-react@7.37.5 unterstützt nur bis eslint ^9.7 und hat keinen Nachfolger. Festgestellt, nicht geraten: TypeError: contextOrFilename.getFilename is not a function.
  • TypeScript 7 geht noch nicht. typescript-eslint@8 erlaubt >=4.8.4 <6.1.0.
  • shadcn init ist nicht skriptfähig — es fragt nach einem Preset, auch mit --yes, und bleibt hinter einer Pipe hängen. shadcn add <bauteil> --yes --overwrite < /dev/null funktioniert dagegen. Die Registry importiert den Helfer als blanken Modulnamen cn, dafür braucht tsconfig.json den Pfad "cn": ["./src/lib/utils"], und die Bauteile brauchen das Paket radix-ui.
  • Port 3000 war von ProxLedger belegt, die Abnahme lief deshalb auf 3100 bzw. 3200.
  • Die Migrationen laufen im Compose-Stand nachweislich automatisch; eingespielt wird ab #006 etwas, vorher meldet der Dienst korrekt „No pending migrations to apply“.