Commit Graph
81 Commits
Author SHA1 Message Date
Florian WildeandClaude Opus 5 36370f8ff8 docs: Handbuchbilder für M2 neu aufnehmen
36 Bilder aus der Wegwerf-Instanz: neun Seiten, je hell und dunkel,
Schreibtisch und Handy. Ausschließlich erfundene Daten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:46:13 +02:00
Florian WildeandClaude Opus 5 d01c49b5a0 docs: Handbuchseiten zu Aufgaben, Ideen und Projekten
Jede Seite erklärt nicht nur, wo man klickt, sondern warum es so gebaut
ist: warum die Schnellerfassung nur ein Feld hat, warum die Inbox kein
eigener Zustand ist, warum Ideen getrennt liegen und warum Verworfenes
nicht in den Fortschritt zählt.

README und Schnellstart auf den Stand von M2 gebracht; der Schnellstart
endet jetzt mit der ersten erfassten Aufgabe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:33:34 +02:00
Florian WildeandClaude Opus 5 4d0f83ff51 chore: Demo-Daten für die neuen Seiten erweitern
Erfundene Aufgaben mit einer Mischung aus überfällig, heute, später und
ohne Termin, dazu Inbox, Ideen und ein Projekt mit Arbeitspaketen und
einem Meilenstein. Die Fälligkeiten stehen als Versatz zum festen
Demo-Zeitpunkt, damit die Bilder zwischen zwei Läufen gleich bleiben.

Eine Aufgabe trägt eine erfundene Zammad-Herkunft, damit die Darstellung
der Quelle in den Bildern zu sehen ist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:33:34 +02:00
Florian WildeandClaude Opus 5 144457b9f0 fix(aufgaben): Ideen aus Aufgabenliste und Inbox heraushalten
Der Fehler fiel erst in den aufgenommenen Bildern auf: Ideen standen
mitten in der Aufgabenliste, obwohl der ganze Sinn der getrennten
Ideenseite ist, dass sie das nicht tun. Ohne Termin hätten sie dort
entweder unsichtbar unten gestanden oder mit erfundenem Termin oben.

Der Filter kennt jetzt ohneIdeen; ein ausdrücklich gewählter Typ gewinnt,
wer nach Ideen filtert, sieht sie also weiterhin.

Die Startseite filterte Ideen bisher von Hand nach dem Laden — das
erledigt jetzt dieselbe Abfrage wie überall sonst.

Ein E2E-Test hält den Fall fest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:33:33 +02:00
Florian WildeandClaude Opus 5 7c8fe10f35 test: Erfassungsweg und Eindeutigkeit der Quellen absichern
E2E über den Weg, der in M2 zählt: schnell erfassen, einsortieren,
abhaken — und nachsehen, dass Erledigtes aus der Standardansicht fällt,
aber mit Filter wieder auftaucht.

Der Integrationstest deckt jetzt auch ab, was die Integrationen ab M6
brauchen: dieselbe Quelle mit derselben Kennung nur einmal, dieselbe
Kennung bei anderer Quelle erlaubt, mehrere Aufgaben ohne Quellverweis
nebeneinander, und ein Upsert aktualisiert statt zu doppeln.

Dazu der Nachweis, dass Aufgaben das Löschen ihres Projekts und ihres
Bereichs überleben und dabei nur die Zuordnung verlieren.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:17:45 +02:00
Florian WildeandClaude Opus 5 a4a889fcb8 feat(aufgaben): Inbox, Aufgabenliste, Ideen und Projekte
Die Schnellerfassung braucht ein Feld und einen Knopf; wer beim Erfassen
erst ein Formular ausfüllen muss, erfasst nichts. Der Fokus bleibt danach
im Feld, damit mehrere Sachen hintereinander hineingehen.

Die Filter der Aufgabenliste stehen in der Adresse, nicht im Zustand der
Komponente — so lässt sich eine gefilterte Liste als Lesezeichen ablegen,
und der Zurück-Knopf tut, was er soll.

Die Startseite zeigt jetzt, was überfällig, heute oder morgen fällig ist,
statt nur die Bereiche aufzuzählen.

Für die Auswahlfelder bewusst das native <select>: es bedient sich auf
dem Handy mit dem Systemdialog und ist ohne Zusatzcode tastaturbedienbar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:17:44 +02:00
Florian WildeandClaude Opus 5 b3ca1c9ce7 feat(aufgaben): Server Actions und Abfrageschicht
Anlegen, ändern, Status setzen, einsortieren, aus einer Idee eine Aufgabe
machen und löschen — jede Aktion prüft die Rolle selbst und hinterlässt
einen Audit-Eintrag.

Statuswechsel gehen nur die Wege, die uebergangErlaubt() zulässt:
Abgeschlossenes wird erst wieder geöffnet, bevor es weitergeht. Wer per
Formular etwas anderes einreicht, bekommt eine Meldung statt eines stillen
Zustandssprungs.

Sortiert wird in der Anwendung, nicht in SQL. Die Regel ist mehrstufig
und steht als geprüfte reine Funktion in src/lib — zwei Fassungen
derselben Regel wären eine Fehlerquelle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:17:43 +02:00
Florian WildeandClaude Opus 5 63c3c15a3c feat(aufgaben): Datenmodell und Regeln für Aufgaben und Projekte
Aufgabe mit Typ, Status, Priorität, Aufwand, Fälligkeit und Herkunft;
Projekt als eigene Klammer darum.

Zwei Entwurfsentscheidungen:
- Die Inbox ist kein eigener Zustand, sondern schlicht eine Aufgabe ohne
  Bereich. Einsortieren heißt: einen Bereich setzen. Ein zusätzliches
  Feld könnte mit der Zuordnung auseinanderlaufen.
- Projekt ist ein eigenes Modell, kein Aufgabentyp. Ein Projekt hat
  selbst weder Aufwand noch Fälligkeit, sondern leitet beides aus dem
  ab, was drinsteckt. ARBEITSPAKET und MEILENSTEIN sind Typen der
  Aufgaben *innerhalb* eines Projekts.

@@unique([quelle, quelleRef]) ist die Grundlage für die Integrationen ab
M6: sie upserten darüber, statt zu doppeln. Von Hand erfasste Aufgaben
tragen quelleRef = null; PostgreSQL sieht NULL-Werte als verschieden an,
sie behindern sich also nicht.

Verworfene Aufgaben zählen bewusst gar nicht in den Fortschritt eines
Projekts — sonst sähe es fertig aus, weil die Hälfte gestrichen wurde.

Fälligkeit wird in Kalendertagen in Europe/Berlin gerechnet, nicht in
24-Stunden-Schritten: was heute um 23:00 fällig ist, ist heute fällig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:17:41 +02:00
Florian WildeandClaude Opus 5 704c490368 Merge: ROADMAP-Status für M1 nachziehen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 15:54:31 +02:00
Florian WildeandClaude Opus 5 01ab74f71d docs: M1 in der ROADMAP als erledigt führen
Der Status stand noch auf "in Arbeit", obwohl 0.1.0 getaggt und nach main
gemergt ist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 15:54:31 +02:00
Florian WildeandClaude Opus 5 56e07a40e8 Merge: Meilenstein 1 – Fundament (0.1.0)
Anmeldung über OIDC, Passkeys und Passwort, Rollen mit serverseitiger
Prüfung in Seite und Server Action, Audit-Protokoll, Instanzsteuerung,
Bereiche, Betrieb über Docker Compose sowie die vollständige Test- und
Dokumentationskette.

Geprüft vor dem Merge: npm run pruefen, 19 Integrationsprüfungen,
Migrationstest und 9 E2E-Tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 15:52:14 +02:00
Florian WildeandClaude Opus 5 8ab4b97655 chore(release): 0.1.0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v0.1.0
2026-09-10 15:51:52 +02:00
Florian WildeandClaude Opus 5 b4d8c19ce4 docs: DEVLOG-Eintrag für M1
Ziel, Erledigtes, die Entscheidungen mit Begründung, die Probleme samt
Lösung und die offenen Punkte. Dazu die eigenen Fehler dieser Sitzung —
die Commits auf main statt auf dem Meilenstein-Zweig, der vermischte
Doku-Commit und das ungeprüfte Skript — damit sie nicht wiederkehren.

Dauer laut zeiten.csv: 161 Minuten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:48:55 +02:00
Florian WildeandClaude Opus 5 688d9f59a8 docs: Handbuchbilder aufnehmen
20 Bilder aus der Wegwerf-Instanz: fünf Seiten, je hell und dunkel,
Schreibtisch und Handy. Ausschließlich erfundene Daten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:46:16 +02:00
Florian WildeandClaude Opus 5 6d04bc2c1c fix(oberflaeche): Kopfleiste einzeilig halten, Demo-Uhr feststellen
Zwei Mängel, die erst in den aufgenommenen Bildern auffielen.

Auf Handybreite brach die Kopfleiste um und drängte den Namen der
Anwendung in die zweite Zeile, mitten zwischen die Menüpunkte. Sie bleibt
jetzt auf allen Breiten einzeilig; wird es eng, scrollt die Navigation
waagerecht.

Das Tagesdatum auf der Startseite wird serverseitig gerendert, weshalb
Playwrights Uhr nicht griff — die Bilder hätten sich täglich geändert. In
der Screenshot-Instanz (DEMO_MODUS=true) steht die Uhr jetzt fest. Reine
Funktionen bekommen den Zeitpunkt weiterhin als Parameter; src/server/
zeitgeber.ts ist die eine Stelle, an der er entsteht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:36:35 +02:00
Florian WildeandClaude Opus 5 cf28ec19f5 test: Rollentrennung end-to-end absichern
Legt einen Benutzer ohne Verwaltungsrechte an und prüft, dass der direkte
Aufruf der Protokollseite auf den Hinweis führt — nicht nur, dass der
Menüpunkt fehlt. Sich darauf zu verlassen, dass die Oberfläche Knöpfe
ausblendet, wäre kein Nachweis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:32:52 +02:00
Florian WildeandClaude Opus 5 a88273117c chore: Screenshot-Kette und Demo-Seed
npm run screenshots startet eine Wegwerf-Instanz in einem eigenen
Compose-Projekt mit eigener Datenbank, spielt ausschließlich erfundene
Daten ein, fährt die Seiten mit Playwright ab und räumt am Ende alles
wieder weg. Je Seite entstehen vier Bilder: hell und dunkel, Schreibtisch
und Handy. Die Uhr steht fest, damit sich zwischen zwei Läufen nur das
ändert, was sich wirklich geändert hat.

Der Demo-Seed verweigert den Dienst ohne DEMO_MODUS=true — er würde sonst
erfundene Daten in eine echte Datenbank schreiben.

Test- und Werkzeugordner sind jetzt aus dem Docker-Kontext ausgenommen.
Ein Typfehler in scripts/screenshots.ts hatte den Bau des Abbilds
blockiert, obwohl die Datei dort nichts zu suchen hat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:29:52 +02:00
Florian WildeandClaude Opus 5 e3390c88f4 refactor: middleware in proxy umbenennen
Next 16 hat die Dateikonvention "middleware" abgekündigt und in "proxy"
umbenannt; der Bau warnte bei jedem Lauf. Umbenannt sind Datei und
Funktion, die Aufgabe bleibt dieselbe.

Die E2E-Tests laufen unverändert durch: die Umleitung samt gemerktem Ziel
funktioniert weiter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:29:52 +02:00
Florian WildeandClaude Opus 5 56b5356708 docs: README, Schnellstart, Handbuch, Fahrplan und CHANGELOG
README mit Funktionsumfang, Bildergalerie, allen Umgebungsvariablen als
Tabelle und dem Betrieb inklusive Sicherung und Wiederherstellung.

Der Schnellstart führt in acht Schritten von git clone zur ersten
Anmeldung, jeder Schritt mit erwartetem Ergebnis. Er endet mit dem
Hinterlegen eines Passkeys statt beim ersten Kalenderkonto oder der
ersten Aufgabe — beides gibt es erst mit M3 und M2. Das ist in ROADMAP
und Handbuch so vermerkt, damit niemand danach sucht.

Handbuch mit Seiten zu Anmelden, Bereichen und Protokoll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:29:51 +02:00
Florian WildeandClaude Opus 5 f91d015cc4 feat(betrieb): Abbild, Compose-Stapel und Betriebsskripte
Dreistufiges Dockerfile mit standalone-Ausgabe; im Laufzeitabbild liegen
weder Quelltext noch Bauwerkzeuge, und es läuft unter einem eigenen
Benutzer.

Die Migrationen erledigt ein eigener Einmaldienst aus der Bau-Stufe. Der
Versuch, nur die Prisma-CLI ins Laufzeitabbild zu kopieren, scheiterte an
ihren transitiven Modulen; sie alle mitzunehmen hätte das Abbild
aufgebläht. Der Dienst läuft vor der App und muss sauber durchlaufen.

Zwei Stellen mussten faul werden, weil Next beim Bauen Seitendaten
sammelt und dabei Module lädt, die es zur Bauzeit noch ohne
Umgebungsvariablen gibt:
- Der Prisma-Client entsteht erst beim ersten Zugriff, über einen Proxy.
- Die Auth.js-Konfiguration wird als Funktion übergeben.
Ebenso liest prisma.config.ts die URL über process.env statt über env(),
das beim Bauen ohne Datenbank abbricht.

Dazu backup.sh, update.sh mit Rückrollpfad und scripts/release.sh. Der
Dump wurde nachweislich in eine Wegwerf-Datenbank zurückgespielt und
stimmt zeilengenau mit dem Original überein. Alle Skripte sind
shellcheck-sauber.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 14:08:04 +02:00
Florian WildeandClaude Opus 5 126ad93301 test: Integrations-, Migrations- und E2E-Tests ergänzen
Der Integrationstest prüft gegen eine echte Datenbank, was Vitest nicht
sieht: Eindeutigkeit, Fremdschlüssel, Kaskaden und dass das Protokoll den
gelöschten Benutzer überlebt. Er läuft bewusst ohne umschließende
Transaktion — er prüft absichtlich Fälle, die scheitern müssen, und ein
gescheiterter Befehl bricht in PostgreSQL die ganze Transaktion ab.
Stattdessen tragen alle Testdaten dieselbe Kennung und werden am Ende
wieder entfernt.

Der Migrationstest legt eine Wegwerf-Datenbank an, spielt alle
Migrationen ein und vergleicht das Ergebnis mit dem Schema. Dass er
Abweichungen wirklich bemerkt, wurde mit einem probeweise ergänzten Feld
nachgewiesen.

Der E2E-Rauchtest deckt den Weg ab, der in M1 trägt: Umleitung ohne
Anmeldung samt gemerktem Ziel, Abweisung falscher Zugangsdaten,
erfolgreiche Anmeldung und die Verwaltungsseite.

Dafür neu: eine Middleware, die nur den angefragten Pfad als Kopfzeile
durchreicht. Ein Layout kennt seinen Pfad nicht, braucht ihn aber für die
Rückkehr nach der Anmeldung. Anmeldelogik steht dort bewusst nicht — die
Middleware läuft im Edge-Umfeld ohne Prisma.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:10:54 +02:00
Florian WildeandClaude Opus 5 24a799d6ba chore: tsconfig-Anpassungen von Next 16 übernehmen
Next setzt beim Bauen jsx auf react-jsx und ergänzt seinen Typpfad. Wird
das zurückgedreht, schreibt es der nächste Build wieder hin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:03:06 +02:00
Florian WildeandClaude Opus 5 1876ae4c6b feat(betrieb): Health-Endpunkt und Seed für eine frische Datenbank
/api/health meldet Status, Version, Instanz und Erreichbarkeit der
Datenbank – ohne Anmeldung, deshalb ohne Zählstände, Namen oder Adressen.
update.sh wird sich später darauf stützen.

Der Seed legt die vier Bereiche an und ein erstes Verwaltungskonto aus
ADMIN_EMAIL und ADMIN_PASSWORT. Er läuft mehrfach ohne Schaden; ohne
ADMIN_PASSWORT entsteht ein Konto, das sich nur über OIDC oder Passkey
anmelden kann.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:02:49 +02:00
Florian WildeandClaude Opus 5 e9a2b4b463 feat(bereiche): Bereiche verwalten mit Rollenprüfung und Audit
Bereiche mit Farbe, Arbeitsfenster und Tageskapazität – die erste
schreibende Funktion und damit der Beleg, dass die Kette trägt: zod prüft
jede Eingabe, die Rolle wird in der Seite und noch einmal in der Server
Action geprüft, jede Änderung hinterlässt einen Audit-Eintrag.

Die Audit-Details werden vor dem Speichern gesäubert: Felder, deren Name
nach Passwort, Token, Betreff oder Mailinhalt aussieht, werden ersetzt.
Das gilt auch in verschachtelten Objekten und Listen.

Dazu die Protokollseite für die Verwaltung und die Startseite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:02:49 +02:00
Florian WildeandClaude Opus 5 cc013fd6a1 feat(anmeldung): OIDC, Passkeys und Passwort mit Auth.js v5
Drei Wege hinein, je Instanz zuschaltbar: generischer OIDC-Anbieter
(Entra ID beziehungsweise authentik), Passkeys und wahlweise E-Mail mit
Passwort.

Entscheidungen:
- Passkeys laufen direkt über SimpleWebAuthn 14, nicht über den
  experimentellen WebAuthn-Provider von Auth.js, der noch auf Fassung 9
  festgelegt ist.
- Eigener Prisma-Adapter, weil der mitgelieferte fest auf prisma.user und
  prisma.account zugreift, unsere Modelle aber Benutzer und Konto heißen.
- Sitzungen als JWT, weil Credentials-Provider das verlangen. Damit eine
  Sperre trotzdem sofort wirkt, liest der session-Callback Rolle und
  Status bei jeder Anfrage frisch aus der Datenbank. Die Tabellen sitzung
  und verifizierungs_token entfallen deshalb wieder.
- allowDangerousEmailAccountLinking ist bewusst an: je Instanz gibt es
  genau einen vertrauenswürdigen Anbieter, der die Adresse selbst prüft.

Anmeldung, Passwort und Passkey antworten bei Fehlschlag immer gleich,
damit sich nicht abfragen lässt, wer hier ein Konto hat. Die
WebAuthn-Herausforderung wird nach einem Versuch verworfen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:02:34 +02:00
Florian WildeandClaude Opus 5 4ff67c15c2 feat(oberflaeche): Grundlayout, Navigation und Farbschema
Deutsche Oberfläche, Hell und Dunkel über next-themes, Navigation mit
Rollenfilter und die shadcn-Primitiven, die M1 braucht.

Der Farbschema-Knopf kommt ohne Effekt und ohne Zustand aus: welches
Symbol gilt, entscheidet die dark-Klasse und damit CSS. next-themes setzt
sie schon vor der Hydratation, deshalb blitzt nichts auf — und die
React-Compiler-Regel gegen setState im Effekt bleibt eingehalten.

Farben als OKLCH, damit Hell und Dunkel dieselbe wahrgenommene Helligkeit
halten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 13:02:34 +02:00
Florian WildeandClaude Opus 5 19ca0826e6 feat(zeit): Zeitrechnung und Anzeigeformate in Europe/Berlin
Tagesgrenzen, Wochenbeginn am Montag und Wochentagsindex passend zu den
Kapazitätsfeldern eines Bereichs. Alle Funktionen bekommen den Zeitpunkt
als Parameter, lesen also nie selbst die Uhr.

Die Tests laufen bewusst in UTC, damit sofort auffällt, wenn irgendwo die
Ortszeit des Servers durchschlägt; sie decken beide Zeitzonen-Halbjahre
und die Umstellungswoche ab.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:41:30 +02:00
Florian WildeandClaude Opus 5 e2b00f4a57 feat(instanz): Instanzsteuerung mit serverseitigem Riegel
INSTANZ entscheidet, welche Kalenderanbieter, Postfächer und
Integrationen es gibt. Was eine Instanz nicht darf, liefert dort 404
statt eines Hinweises. Ein falsch geschriebener Wert bricht ab, statt
still auf einen Standard zu fallen.

Die Konfiguration wird mit zod geprüft; Fehlermeldungen nennen den
Variablennamen, nie den Wert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:41:29 +02:00
Florian WildeandClaude Opus 5 748d032f70 feat(datenbank): Datenmodell für Anmeldung, Bereiche und Audit
Benutzer mit Rolle, OIDC-Konten, Sitzungen, Passkeys samt kurzlebiger
WebAuthn-Herausforderung, Bereiche mit Tageskapazität und Arbeitsfenster
sowie ein unveränderlicher Audit-Eintrag je schreibender Aktion.

Aufgabe und Zeitblock kommen mit M2 und M4, Kalenderkonto mit M3 — dann
mit den Feldern, die der jeweilige Provider wirklich braucht.

Prisma 7 nimmt die Verbindung nicht mehr aus dem Schema entgegen: die URL
steht in prisma.config.ts, der Client bekommt den pg-Treiberadapter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:41:29 +02:00
Florian WildeandClaude Opus 5 de1f923767 chore: Projektgerüst und Werkzeugkette einrichten
Next 16 mit App Router und standalone-Ausgabe, React 19, TypeScript strict,
Tailwind 4, Vitest und Playwright. Alle Versionen exakt gepinnt.

Abweichungen vom ursprünglich notierten Stand, jeweils begründet:
- Prisma 7.10.0 statt 6, weil 6 überholt ist und 8 nur als RC vorliegt.
- TypeScript 6.0.3 statt 7, weil typescript-eslint 8 nur "<6.1.0" trägt.
- ESLint 9.39.5 statt 10, weil das in eslint-config-next gebündelte
  eslint-plugin-react noch context.getFilename() benutzt.
- npm-overrides für SimpleWebAuthn und nodemailer, weil next-auth dort
  ältere optionale Peers deklariert. Die betroffenen next-auth-Provider
  werden nicht benutzt: Passkeys laufen direkt über SimpleWebAuthn 14.

"npm run pruefen" bündelt Prettier, ESLint, tsc, Vitest und ShellCheck.
ShellCheck läuft über das Docker-Abbild, wenn es lokal fehlt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 12:41:28 +02:00
Florian Wilde 8220adc449 chore: Repository anlegen 2026-09-10 12:40:52 +02:00