Ziel, Erledigtes, die Entwurfsentscheidungen mit Begründung, die Probleme samt Lösung und die offenen Punkte. Dazu die eigenen Fehler dieser Sitzung. Dauer laut zeiten.csv: 44 Minuten. Gesamt bislang 205 Minuten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
229 lines
13 KiB
Markdown
229 lines
13 KiB
Markdown
# Entwicklungstagebuch
|
||
|
||
Neuester Eintrag oben. Die Dauer je Sitzung stammt aus `devlog/zeiten.csv`,
|
||
die Übersicht aus `scripts/zeit.sh summe`.
|
||
|
||
## Zeit je Meilenstein
|
||
|
||
| Meilenstein | Art | Minuten | Stunden |
|
||
| ----------- | ------ | ------- | -------- |
|
||
| M1 | claude | 161 | 2,68 |
|
||
| M2 | claude | 44 | 0,73 |
|
||
| **Gesamt** | | **205** | **3,42** |
|
||
|
||
---
|
||
|
||
## 2026-09-10 – M2: Aufgaben und Projekte
|
||
|
||
**Dauer:** 44 Minuten (15:57–16:41), Art `claude`.
|
||
|
||
**Ziel der Sitzung:** Erfassen und Wiederfinden — Aufgaben, Inbox, Ideen und
|
||
Projekte. Noch ohne Kalender.
|
||
|
||
### Erledigt
|
||
|
||
- Datenmodell `Aufgabe` und `Projekt` samt Migration
|
||
- Schnellerfassung, Inbox mit Einsortieren, Aufgabenliste mit Filtern
|
||
- Ideen als eigene Ansicht, mit Übernahme in eine Aufgabe
|
||
- Projekte mit Arbeitspaketen, Meilensteinen und Fortschritt
|
||
- Startseite zeigt jetzt, was überfällig oder demnächst fällig ist
|
||
- 53 neue Unit-Tests, 11 neue Integrationsprüfungen, 4 neue E2E-Tests
|
||
- Handbuchseiten zu Aufgaben, Ideen und Projekten; Bilder neu aufgenommen
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Die Inbox ist kein eigener Zustand.** Eine Aufgabe liegt genau dann in der
|
||
Inbox, wenn sie keinen Bereich hat. Ein zusätzliches Feld „einsortiert"
|
||
hätte mit der Zuordnung auseinanderlaufen können; so ist der Zustand nicht
|
||
darstellbar.
|
||
|
||
**Projekt als eigenes Modell, nicht als Aufgabentyp.** Ein Projekt hat
|
||
selbst weder Aufwand noch Fälligkeit — beides ergibt sich aus dem Inhalt.
|
||
`ARBEITSPAKET` und `MEILENSTEIN` bleiben Typen der Aufgaben _innerhalb_
|
||
eines Projekts.
|
||
|
||
**Verworfene Aufgaben zählen gar nicht in den Fortschritt.** Weder als
|
||
erledigt noch als offen. Sonst sähe ein Projekt fertig aus, weil die Hälfte
|
||
gestrichen wurde — eine Zahl, die lügt, ist schlechter als keine.
|
||
|
||
**Sortiert wird in der Anwendung, nicht in SQL.** Die Reihenfolge ist
|
||
mehrstufig (offen vor abgeschlossen, dann Termin, dann Priorität, dann Titel
|
||
mit deutscher Sortierung). Als geprüfte reine Funktion steht sie an einer
|
||
Stelle; eine zweite Fassung in SQL wäre eine Fehlerquelle. Bei den erwarteten
|
||
Mengen ist das unkritisch — sollte es einmal eng werden, ist der Ort für die
|
||
Änderung klar.
|
||
|
||
**Filter stehen in der Adresse.** Eine gefilterte Liste lässt sich als
|
||
Lesezeichen ablegen, und der Zurück-Knopf tut, was er soll.
|
||
|
||
**Statuswechsel folgen festen Übergängen.** Aus `ERLEDIGT` und `VERWORFEN`
|
||
geht es nur zurück nach `OFFEN`. Wer per Formular etwas anderes einreicht,
|
||
bekommt eine Meldung — nicht einen stillen Zustandssprung.
|
||
|
||
**Fälligkeit in Kalendertagen, nicht in Stunden.** Was heute um 23:00 fällig
|
||
ist, ist heute fällig. Alles andere fühlt sich beim Benutzen falsch an.
|
||
|
||
**Natives `<select>` statt einer eigenen Auswahlkomponente.** Es bedient
|
||
sich auf dem Handy mit dem Systemdialog, ist ohne Zusatzcode
|
||
tastaturbedienbar und braucht kein JavaScript zum Absenden.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| ESLint beanstandete `String(antwort.meldung)` | Ich hatte die Rückmeldung als `Record<string, unknown>` typisiert, statt den vorhandenen `Rueckmeldung`-Typ zu nehmen. Mit dem richtigen Typ entfällt die Umwandlung ganz. |
|
||
| Zwei Stellen rechneten die Tagesgrenze mit einem `toLocaleString`-Trick | `src/lib/zeit.ts` konnte das längst sauber. Beide Stellen nutzen jetzt `tagesBeginn()` beziehungsweise `alsTagesschluessel()`; die Tests belegen gleiche Ergebnisse bei weniger Code. |
|
||
| Typfehler nach der Migration | Der Prisma-Client muss nach jeder Schemaänderung neu erzeugt werden. `npm run pruefen` tut das inzwischen selbst, wenn er fehlt — hier war er nur veraltet. |
|
||
|
||
### Eigene Fehler, die ich korrigiert habe
|
||
|
||
- Im Demo-Seed stand versehentlich ein kyrillisches Wort als Titel eines
|
||
Meilensteins. Ersetzt durch „Seite ist online".
|
||
- Ein Test erwartete, dass 481 Minuten Aufwand erlaubt sind; die Grenze
|
||
liegt bei 480. Der Test war falsch, nicht der Code — jetzt prüft er die
|
||
Grenze selbst und den ersten Wert darüber.
|
||
|
||
### Offene Punkte
|
||
|
||
- Kein Wiederholungsrhythmus für Aufgaben und keine Verknüpfung von Aufgaben
|
||
untereinander. Beides steht bewusst unter „Ideen für später" und wartet
|
||
darauf, ob es im Alltag wirklich fehlt.
|
||
- Die Aufgabenliste lädt bis zu 500 Einträge auf einmal. Für den erwarteten
|
||
Bestand reicht das; eine Blätterfunktion kommt, wenn es eng wird.
|
||
|
||
### Commits
|
||
|
||
- 63c3c15 feat(aufgaben): Datenmodell und Regeln für Aufgaben und Projekte
|
||
- b3ca1c9 feat(aufgaben): Server Actions und Abfrageschicht
|
||
- a4a889f feat(aufgaben): Inbox, Aufgabenliste, Ideen und Projekte
|
||
- 7c8fe10 test: Erfassungsweg und Eindeutigkeit der Quellen absichern
|
||
- 144457b fix(aufgaben): Ideen aus Aufgabenliste und Inbox heraushalten
|
||
- 4d0f83f chore: Demo-Daten für die neuen Seiten erweitern
|
||
- d01c49b docs: Handbuchseiten zu Aufgaben, Ideen und Projekten
|
||
|
||
---
|
||
|
||
## 2026-09-10 – M1: Fundament
|
||
|
||
**Dauer:** 161 Minuten (12:06–14:47), Art `claude`.
|
||
|
||
**Ziel der Sitzung:** Das Projekt von Grund auf aufsetzen: Gerüst,
|
||
Datenbank, Anmeldung, Rollen, Audit, Instanzsteuerung, Betrieb und
|
||
Dokumentation.
|
||
|
||
### Erledigt
|
||
|
||
- Projektgerüst mit Next 16, React 19, TypeScript strict, Tailwind 4,
|
||
alle Versionen exakt gepinnt
|
||
- `npm run pruefen` als Sammelprüfung: Prettier, ESLint, `tsc`, Vitest,
|
||
ShellCheck
|
||
- Datenmodell und erste Migration: Benutzer mit Rolle, OIDC-Konten,
|
||
Passkeys, WebAuthn-Herausforderungen, Bereiche, Audit
|
||
- Anmeldung über OIDC, Passkey und Passwort, je Instanz zuschaltbar
|
||
- Rollenprüfung in Seite **und** Server Action, Audit je Schreibaktion
|
||
- Instanzsteuerung mit 404 statt Hinweis
|
||
- Bereiche verwalten, Protokollseite, Startseite, Einstellungen
|
||
- `/api/health`, Dockerfile, Compose-Stapel mit Migrationsdienst
|
||
- `backup.sh`, `update.sh`, `scripts/release.sh`, `scripts/zeit.sh`
|
||
- Unit-, Integrations-, Migrations- und E2E-Tests
|
||
- Screenshot-Kette mit eigener Wegwerf-Instanz
|
||
- README, Schnellstart, Handbuch, ROADMAP, CHANGELOG
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Prisma 7.10.0 statt der notierten Fassung 6.** Fassung 6 ist überholt,
|
||
Fassung 8 liegt nur als RC auf `latest`. Prisma 7 nimmt die Verbindung
|
||
nicht mehr im Schema entgegen — sie steht in `prisma.config.ts`, der
|
||
Client bekommt den pg-Treiberadapter.
|
||
|
||
**TypeScript 6.0.3 statt 7.0.2.** TS 7 ist zwar `latest`, aber
|
||
`typescript-eslint@8.70` verlangt `typescript >=4.8.4 <6.1.0`. Mit TS 7
|
||
wäre die Lint-Kette kaputt gewesen.
|
||
|
||
**ESLint 9.39.5 statt 10.** Das in `eslint-config-next@16` gebündelte
|
||
`eslint-plugin-react` ruft noch `context.getFilename()`, das ESLint 10
|
||
entfernt hat. Der Lint-Lauf brach reproduzierbar ab.
|
||
|
||
**Eigener Auth.js-Adapter.** Der mitgelieferte `@auth/prisma-adapter`
|
||
greift fest auf `prisma.user` und `prisma.account` zu; unsere Modelle
|
||
heißen `Benutzer` und `Konto`. Der Adapter ist schmal und ersparte es,
|
||
die Fachbegriffe der Konvention zu opfern.
|
||
|
||
**Passkeys direkt über SimpleWebAuthn 14.** Der WebAuthn-Provider von
|
||
Auth.js ist als experimentell markiert und auf SimpleWebAuthn 9
|
||
festgelegt. Direkt angebunden bleibt die Fassung aktuell und der Ablauf
|
||
nachvollziehbar. Für die npm-Auflösung brauchte es `overrides`, weil
|
||
next-auth die älteren Fassungen als optionale Peers deklariert.
|
||
|
||
**Sitzungen als JWT, Rolle trotzdem aus der Datenbank.** Credentials-
|
||
Provider verlangen JWT. Damit eine Sperre nicht bis zum Ablauf des Tokens
|
||
wirkungslos bleibt, liest der `session`-Callback Rolle und Status bei
|
||
jeder Anfrage frisch. Die Tabellen `sitzung` und `verifizierungs_token`
|
||
wären damit tot und wurden wieder entfernt, solange es nur eine einzige
|
||
Migration gab.
|
||
|
||
**Migrationen als eigener Compose-Dienst.** Der Versuch, nur die
|
||
Prisma-CLI ins schlanke Laufzeitabbild zu kopieren, scheiterte an ihren
|
||
transitiven Modulen (`effect` über `@prisma/config`). Alle mitzunehmen
|
||
hätte das Abbild aufgebläht. Ein Einmaldienst aus der Bau-Stufe läuft vor
|
||
der App und muss sauber durchlaufen.
|
||
|
||
**Aufgabe, Zeitblock und Kalenderkonto bewusst nicht modelliert.** Sie
|
||
kommen mit M2, M4 und M3 — dann mit den Feldern, die der jeweilige
|
||
Provider wirklich braucht, statt heute geraten.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `prisma validate` lehnte `url` im Schema ab | Prisma 7 erwartet die Verbindung in `prisma.config.ts` und einen Treiberadapter im Client. |
|
||
| Integrationstest brach nach der ersten erwarteten Verletzung ab | In PostgreSQL bricht ein fehlgeschlagener Befehl die ganze Transaktion ab. Der Test läuft jetzt ohne Sammeltransaktion; alle Testdaten tragen eine Kennung und werden am Ende entfernt. |
|
||
| `prisma migrate diff` kannte `--from-url` nicht | In Prisma 7 heißen die Flaggen anders. Der Test vergleicht jetzt die eingespielte Wegwerf-Datenbank über `--from-config-datasource` gegen das Schema — und braucht damit keine Schattendatenbank. |
|
||
| Nach der Anmeldung ging es nicht zum ursprünglichen Ziel zurück | Das Layout leitet um, bevor die Seite drankommt, und kennt seinen Pfad nicht. Ein schlanker Proxy reicht ihn als Kopfzeile durch — ohne Anmeldelogik, weil er im Edge-Umfeld läuft. |
|
||
| Der Docker-Bau brach beim Sammeln der Seitendaten ab | Prisma-Client und Auth.js-Konfiguration wurden beim Laden des Moduls ausgewertet und brauchten Umgebungsvariablen, die es zur Bauzeit nicht gibt. Beide entstehen jetzt faul. |
|
||
| Ein Typfehler in `scripts/screenshots.ts` blockierte den Abbild-Bau | Entwicklungsskripte gehören nicht in den Docker-Kontext. `.dockerignore` schließt sie aus; geprüft werden sie von `npm run pruefen`. |
|
||
| ESLint beanstandete `setState` im Effekt | Der Hydratations-Trick im Farbschema-Knopf war unnötig: `next-themes` setzt die `dark`-Klasse vor der Hydratation, das erledigt CSS ohne Zustand. |
|
||
|
||
### Eigene Fehler, die ich korrigiert habe
|
||
|
||
- Die ersten vier Commits lagen direkt auf `main` statt auf einem
|
||
Meilenstein-Zweig und ohne Attributionszeile. Historie umgebaut:
|
||
`main` trägt nur noch einen leeren Wurzel-Commit, die Arbeit liegt auf
|
||
`m1-fundament`.
|
||
- Ein `git add -A` zog die Proxy-Umbenennung in den Doku-Commit. Wieder
|
||
getrennt.
|
||
- `scripts/screenshots.ts` wurde geschrieben, ohne danach `npm run
|
||
pruefen` laufen zu lassen; der Typfehler fiel erst im Docker-Bau auf.
|
||
|
||
### Offene Punkte
|
||
|
||
- Die `origin`-URL fehlt noch. Push und Merge stehen ohnehin erst nach
|
||
der Freigabe an.
|
||
- Der Schnellstart endet beim Passkey statt beim ersten Kalenderkonto und
|
||
der ersten Aufgabe — beides gibt es erst mit M3 und M2.
|
||
- Der Demo-Seed enthält noch keine erfundenen Termine; einen
|
||
Demo-Kalenderprovider gibt es erst mit M3.
|
||
- Lokal weichen die Ports ab (Datenbank 5435, App 3001), weil 5432 und
|
||
3000 auf diesem Rechner belegt sind. In `.env.example` stehen die
|
||
üblichen Werte.
|
||
|
||
### Commits
|
||
|
||
- de1f923 chore: Projektgerüst und Werkzeugkette einrichten
|
||
- 748d032 feat(datenbank): Datenmodell für Anmeldung, Bereiche und Audit
|
||
- e2b00f4 feat(instanz): Instanzsteuerung mit serverseitigem Riegel
|
||
- 19ca082 feat(zeit): Zeitrechnung und Anzeigeformate in Europe/Berlin
|
||
- 4ff67c1 feat(oberflaeche): Grundlayout, Navigation und Farbschema
|
||
- cc013fd feat(anmeldung): OIDC, Passkeys und Passwort mit Auth.js v5
|
||
- e9a2b4b feat(bereiche): Bereiche verwalten mit Rollenprüfung und Audit
|
||
- 1876ae4 feat(betrieb): Health-Endpunkt und Seed für eine frische Datenbank
|
||
- 24a799d chore: tsconfig-Anpassungen von Next 16 übernehmen
|
||
- 126ad93 test: Integrations-, Migrations- und E2E-Tests ergänzen
|
||
- f91d015 feat(betrieb): Abbild, Compose-Stapel und Betriebsskripte
|
||
- 56b5356 docs: README, Schnellstart, Handbuch, Fahrplan und CHANGELOG
|
||
- e3390c8 refactor: middleware in proxy umbenennen
|
||
- a882731 chore: Screenshot-Kette und Demo-Seed
|
||
- cf28ec1 test: Rollentrennung end-to-end absichern
|
||
- 6d04bc2 fix(oberflaeche): Kopfleiste einzeilig halten, Demo-Uhr feststellen
|
||
- 688d9f5 docs: Handbuchbilder aufnehmen
|