700 lines
42 KiB
Markdown
700 lines
42 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 |
|
||
| M3 | claude | 41 | 0,68 |
|
||
| M4 | claude | 167 | 2,78 |
|
||
| M5 | claude | 420 | 7,00 |
|
||
| M6 | claude | 733 | 12,22 |
|
||
| M7 | claude | 886 | 14,77 |
|
||
| **Gesamt** | | **2452** | **40,87** |
|
||
|
||
---
|
||
|
||
## 2026-09-13 – M7: Schnellerfassung und API
|
||
|
||
**Dauer:** 886 Minuten (10:15–01:01), Art `claude`.
|
||
|
||
> Wie schon bei M6 sagt die Zahl mehr über den Rechner als über die Arbeit:
|
||
> Fünf hängende Docker-Bauten, einer davon mit einem einzelnen Schritt über
|
||
> drei Stunden. Die Bildaufnahme musste achtmal angestoßen werden.
|
||
|
||
**Ziel der Sitzung:** Etwas festhalten können, wo man gerade steht — und
|
||
andere Anwendungen hereinlassen, ohne ihnen die Schlüssel zum ganzen Haus zu
|
||
geben.
|
||
|
||
### Erledigt
|
||
|
||
- Regeln der Schnittstelle als reine Logik: Rechte, Tokenaufbau, Eingaben
|
||
(30 Tests)
|
||
- `/api/v1`: Aufgaben lesen, anlegen, ändern, abhaken
|
||
- Tokens mit Rechten und wahlweise Ablauf, verwaltbar in der Anwendung
|
||
- Schnellerfassung fürs Handy mit Warteschlange in IndexedDB
|
||
- Manifest und Symbol: der Planer lässt sich als App ablegen
|
||
- 10 neue E2E-Tests, 6 neue Integrationsprüfungen, Handbuchseite, API-Doku
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Von einem Token liegt nur der Hash in der Datenbank.** Ein Token ist ein
|
||
Passwort; es gibt keinen Grund, es zurücklesen zu können, und jeden Grund,
|
||
es nicht zu können. SHA-256 ohne Salz ist hier richtig — der Wert hat 32
|
||
Byte Zufall, gegen so etwas hilft kein Wörterbuch, und die Prüfung muss bei
|
||
jeder Anfrage schnell sein.
|
||
|
||
**Kein Recht schließt ein anderes ein.** „Erfassen" darf nicht lesen. Ein
|
||
Dienst, der nur meldet „hier ist etwas Neues", braucht den Bestand nicht zu
|
||
sehen — und das ist der häufigste Fall.
|
||
|
||
**Der sichtbare Anfang heißt `planer_`.** Wer so einen Wert versehentlich in
|
||
ein öffentliches Verzeichnis schiebt, soll ihn beim Durchsehen erkennen.
|
||
|
||
**`/selbst` braucht kein Recht.** Sonst müsste man beim Einrichten raten, ob
|
||
der Token falsch ist oder nur zu wenig darf.
|
||
|
||
**Die Schnittstelle kann Bereich und Projekt nicht setzen.** Was von außen
|
||
kommt, landet in der Inbox. Die Einordnung ist Sache dessen, der plant,
|
||
nicht dessen, der meldet. Löschen kann sie auch nicht: Ein Fehler im fremden
|
||
Skript soll keine Planung vernichten.
|
||
|
||
**Erst anstellen, dann senden.** Die Schnellerfassung schreibt zuerst in die
|
||
Warteschlange des Geräts und versucht dann zu senden. Andersherum wäre der
|
||
Eintrag weg, wenn der Browser mitten im Senden geschlossen wird.
|
||
|
||
**IndexedDB statt localStorage.** Das sind echte Daten des Benutzers. Ein
|
||
Eintrag, der beim Aufräumen des Speichers verschwindet, ist eine verlorene
|
||
Aufgabe — genau das, was der Planer verhindern soll.
|
||
|
||
**Der Dienstarbeiter hält genau eine Seite vor.** Ein halb veralteter Planer
|
||
wäre schlimmer als gar keiner; ein leeres Eingabefeld dagegen veraltet
|
||
nicht. Und ohne die Seite nützte die Warteschlange niemandem: Man käme gar
|
||
nicht erst hin.
|
||
|
||
**Die Navigation wurde geteilt statt weiter gedehnt.** Nach M6 hatte ich die
|
||
Leiste verbreitert; mit dem dreizehnten Eintrag verschluckte sie erneut
|
||
Punkte. Das war die falsche Antwort: Das Problem ist nicht die Breite,
|
||
sondern die Vermischung. Alltag steht in der Leiste, Einrichtung und
|
||
Verwaltung liegen hinter „Mehr".
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Die Erfassungsseite zählte die Inbox anders als die Kopfleiste | Sie zählte Ideen mit. Jetzt benutzt sie dieselbe Abfrage. Derselbe Fehlertyp wie in M2 — und wieder nur im Bild aufgefallen, im Code sah jede Seite für sich richtig aus. |
|
||
| Die Leiste verschluckte mit dem dreizehnten Eintrag wieder Punkte | Aufgeteilt statt verbreitert, siehe oben. Zwei E2E-Tests greifen den neuen Weg über „Mehr" auf. |
|
||
| Ein E2E-Test brach mit `ERR_ABORTED` ab | Die Erfolgsmeldung kommt, bevor das Neuladen der Liste fertig ist; wer dann navigiert, bricht es ab. Der Test wartet jetzt darauf. |
|
||
| `exactOptionalPropertyTypes` und Prismas Update-Typen vertragen kein `undefined` | Das Datenobjekt wird Feld für Feld gebaut statt per Spread. Ein gesetztes `undefined` ist etwas anderes als ein fehlendes Feld. |
|
||
| Ein Devanagari-Zeichen war in ein Token-Präfix der Demo-Daten geraten | Entfernt. Beim Schreiben über die Shell passiert so etwas; die Datei danach durchsucht. |
|
||
| Fünf hängende Docker-Bauten, einer über drei Stunden | Kein Codeproblem: Nach Abbruch und Neustart lief derselbe Bau in Minuten durch. Zweimal scheiterte zusätzlich das Anlegen des Compose-Netzes. |
|
||
|
||
### Offene Punkte
|
||
|
||
- **Die Bilder der Erfassungsseite zeigen die alte Inbox-Zahl.** Der Bildlauf
|
||
ließ sich nach der Korrektur nicht mehr durchbringen. Ein Lauf, wenn Docker
|
||
wieder mitspielt, räumt das auf.
|
||
- Keine Begrenzung der Aufrufzahl je Token. Bei einer Handvoll eigener
|
||
Tokens wäre sie mehr Zeremonie als Schutz — bei einem Token, der außer
|
||
Haus geht, sähe das anders aus.
|
||
- Die Warteschlange kennt keinen Abbruch: Ein Eintrag, der dauerhaft
|
||
scheitert, wird ewig erneut versucht. Bisher gibt es keinen Fall, in dem
|
||
das passieren könnte, ohne dass gleichzeitig die Anmeldung fehlt.
|
||
|
||
### Commits
|
||
|
||
- d5c7974 feat: Datenmodell und Regeln für die öffentliche Schnittstelle
|
||
- 6d3ebd0 feat: API v1 für Aufgaben
|
||
- b17c42a feat: Tokens verwalten
|
||
- 9565b9a feat: Schnellerfassung vom Handy, auch ohne Netz
|
||
- 10b49cf test: Die Tür der Schnittstelle scharf prüfen
|
||
- 2e57713 docs: Handbuchseite zum Erfassen und Beschreibung der Schnittstelle
|
||
- 7919149 fix: Navigation aufteilen, statt sie weiter zu dehnen
|
||
- f9a616e docs: Handbuchbilder für M7 aufnehmen
|
||
|
||
---
|
||
|
||
## 2026-09-12 – M6: Integrationen
|
||
|
||
**Dauer:** 733 Minuten (20:47–09:00), Art `claude`.
|
||
|
||
> Die Zahl täuscht. Ein großer Teil davon war Warten auf Docker-Bauten, die
|
||
> in der Nacht dreimal hängen blieben — einmal 39 Minuten ohne eine Zeile
|
||
> Ausgabe. Die eigentliche Arbeit war knapper.
|
||
|
||
**Ziel der Sitzung:** Aufgaben dort abholen, wo sie entstehen — ohne dass die
|
||
Planung darunter leidet, die hier passiert.
|
||
|
||
### Erledigt
|
||
|
||
- Gemeinsame Aufnahmestelle für alle Integrationen (21 Tests)
|
||
- Zammad, lesend, mit Suchausdruck aus der Konfiguration
|
||
- Telegram: erfassen, melden, abhaken — mit Kopplung über Einmalcode
|
||
- Spiegel zwischen den Instanzen: Belegung ohne Titel, Aufgaben nur markiert
|
||
- Seite „Integrationen" mit Zustand, Fehler und Knopf für den Sofortlauf
|
||
- Gespiegelte Belegung in der Wochenansicht
|
||
- 4 neue E2E-Tests, 9 neue Integrationsprüfungen, Handbuchseite
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Das Ursprungssystem führt Titel, Notiz, Verweis und Fälligkeit — mehr
|
||
nicht.** Bereich, Projekt, Priorität und geschätzter Aufwand gehören der
|
||
Planung. Wer ein Ticket einsortiert und schätzt, muss sich darauf verlassen
|
||
können, dass der nächste Abgleich das nicht wegräumt. Ohne diese Trennung
|
||
wäre die Wochenplanung nach jedem Lauf kaputt.
|
||
|
||
**Zammad wird nur gelesen.** Ein Ticket schließt man im Ticketsystem, sonst
|
||
weiß die Kollegin nicht, warum es plötzlich zu ist. Umgekehrt darf Zammad
|
||
hier abhaken — dort ist die Wahrheit über den Zustand des Tickets.
|
||
|
||
**Telegram holt ab, statt einen Webhook zu bekommen.** Ein Webhook verlangt
|
||
eine von außen erreichbare Adresse mit gültigem Zertifikat. Der Planer soll
|
||
auch hinter einem Anschluss ohne feste Adresse laufen, und der Preis dafür
|
||
sind zwei Minuten Verzögerung.
|
||
|
||
**Unbekannte Chats bekommen keine Antwort — auch keine Fehlermeldung.** Wer
|
||
die Kennung des Bots kennt, könnte ihm sonst schreiben und daran erkennen,
|
||
dass es diesen Planer gibt. Die einzige Ausnahme ist ein gültiger
|
||
Kopplungscode.
|
||
|
||
**Der Kopplungscode entstand aus einem Widerspruch.** Erst sollte die
|
||
Chatkennung von Hand eingetragen werden. Nur: Um sie zu erfahren, müsste der
|
||
Bot Unbekannten antworten — genau das, was er nicht darf. Der Code dreht die
|
||
Richtung um: Er kommt aus der Anwendung, in die man sich anmelden muss.
|
||
|
||
**Der Spiegel schickt Belegung ohne Titel.** Daheim soll zu sehen sein,
|
||
_dass_ die Zeit weg ist, nicht woran gearbeitet wird. Aufgaben gehen nur
|
||
einzeln markiert mit — ein Schalter für einen ganzen Bereich würde zu leicht
|
||
Kundennamen nach Hause schicken.
|
||
|
||
**Was der Sender nicht mehr schickt, verschwindet beim Empfänger.** Sonst
|
||
stünde daheim für immer eine Sperre, die es längst nicht mehr gibt. Für
|
||
Aufgaben gilt das nicht: Sie sind Planung, keine Sperre.
|
||
|
||
**Jede Sendung ist fünf Minuten gültig.** Ohne Frist ließe sich ein
|
||
abgefangenes Paket beliebig oft erneut einspielen. Der Empfänger antwortet
|
||
auf jeden Fehlschlag gleich — falsches Geheimnis, verstellte Uhr und Unsinn
|
||
im Inhalt sind von außen nicht zu unterscheiden.
|
||
|
||
**Die Demo-Instanz redet mit niemandem.** Sie ist mit erfundenen Daten
|
||
gefüllt und steht nur für Bilder. Solange `DEMO_MODUS` gilt, starten die
|
||
Jobs für Zammad, Telegram und den Spiegel gar nicht erst.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Die Kopfleiste verschluckte mit dem elften Eintrag „Einstellungen" und „Protokoll" | Sie ist jetzt breiter als die Inhaltsspalte. Eine Leiste, die Punkte stillschweigend abschneidet, ist schlimmer als eine, die nicht bündig mit dem Text steht. Gefunden im aufgenommenen Bild. |
|
||
| Der Kartentext erklärte noch den Weg von vor dem Kopplungscode | Ersetzt. Ebenfalls erst im Bild aufgefallen — im Code war beides für sich stimmig. |
|
||
| Ein Kürzungstest schlug an der Grenze fehl | Nicht der Test war falsch, sondern der Vergleich: Eine Worttrennung bei genau der halben Titellänge wurde abgelehnt. Jetzt `>=`. |
|
||
| Ein regulärer Ausdruck ging beim Schreiben über die Shell kaputt | Das Muster steht jetzt als benannte Konstante im Code, geschrieben mit dem Edit-Werkzeug statt über ein Skript. |
|
||
| Ein unsichtbares Zeichen (U+200B) landete mitten in einem Funktionsnamen | Entfernt und die Datei danach abgesucht. |
|
||
| Das Aufräumen im Integrationstest hätte fremde Telegram-Chats treffen können | Es trifft jetzt genau die Kennung, die der Lauf selbst angelegt hat. |
|
||
| Docker-Bauten blieben dreimal hängen, einmal 39 Minuten ohne Ausgabe | Kein Codeproblem: Nach Abbruch und Neustart lief derselbe Bau in Minuten durch. Die Bilder mussten deshalb viermal angestoßen werden. |
|
||
|
||
### Offene Punkte
|
||
|
||
- **Der Wartungsplaner ist nicht angebunden.** Die Aufnahmestelle steht
|
||
bereit und die Seite führt ihn als sichtbare Lücke; es fehlt allein das
|
||
Abholen. Dafür muss erst feststehen, was das Produkt an Schnittstelle
|
||
hergibt.
|
||
- Zammad und Telegram sind gegen die Logik geprüft, nicht gegen die echten
|
||
Dienste — dafür fehlen Zugangsdaten. Der Spiegel dagegen ist end-to-end
|
||
geprüft, weil beide Enden dieselbe Anwendung sind.
|
||
- Ein Telegram-Chat lässt sich nicht umbenennen. Fällt das im Alltag auf,
|
||
ist es ein Einzeiler.
|
||
|
||
### Commits
|
||
|
||
- 7f8f505 feat: Datenmodell und Umgebung für Integrationen
|
||
- e4d3547 feat: Aufnahmeregeln für Integrationen als reine Logik
|
||
- 6dad333 feat: Zammad, Telegram und den Spiegel anbinden
|
||
- bc60e5f feat: Integrationen takten und Telegram als dritten Meldeweg
|
||
- 1281c3e feat: Integrationen sichtbar und bedienbar machen
|
||
- 0d0f850 feat: Gespiegelte Belegung in der Wochenansicht zeigen
|
||
- 4da2f3d test: Spiegel-Eingang und Integrationstabellen absichern
|
||
- 3befcc1 feat: Aufgaben einzeln zum Spiegeln freigeben
|
||
- f5eadef feat: Lauf von Hand anstoßen und Spiegelschalter in der Liste
|
||
- 3c7cdbb chore: Demo-Daten, Bildaufnahme und Platz in der Kopfleiste
|
||
- 475b1d5 docs: Handbuchseite, README und Fahrplan für M6
|
||
- 98d170b docs: Handbuchbilder für M6 neu aufnehmen
|
||
|
||
---
|
||
|
||
## 2026-09-11 – M5: Benachrichtigungen
|
||
|
||
**Dauer:** 420 Minuten (06:35–13:35), Art `claude`.
|
||
|
||
**Ziel der Sitzung:** Sagen, was ansteht — und dabei so selten stören, dass
|
||
niemand auf die Idee kommt, alles abzuschalten.
|
||
|
||
### Erledigt
|
||
|
||
- Regeln als reine Logik: Anlässe, Ruhezeit, Wiederholungssperre (30 Tests)
|
||
- Web-Push über VAPID samt Service Worker, An- und Abmeldung je Browser
|
||
- Mailversand über nodemailer, als Text und als HTML
|
||
- Vier Anlässe: Tagesüberblick, Fälligkeit, Vorlauf vor einem Zeitblock,
|
||
Abgleichfehler
|
||
- Bedienung in den Einstellungen mit Probemeldung und Geräteliste
|
||
- Ein Lauf alle zehn Minuten, Nachweise räumt die Nachtrunde weg
|
||
- 2 neue E2E-Tests, 7 neue Integrationsprüfungen, Handbuchseite
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Ab Werk meldet der Planer gar nichts.** Kein Weg ist eingeschaltet. Wer
|
||
beim ersten Start mit Meldungen überschüttet wird, schaltet alles ab — und
|
||
dann auch das Nützliche. Die Einrichtung ist ein bewusster Handgriff, und
|
||
das ist der Preis dafür, dass die Meldungen später ernst genommen werden.
|
||
|
||
**Der Tagesüberblick ist von der Ruhezeit ausgenommen.** Sonst bekäme ihn
|
||
nie, wer ihn auf 06:00 stellt — die voreingestellte Ruhezeit läuft bis
|
||
07:00. Die Ruhezeit schützt vor Störungen, nicht vor der Nachricht, um die
|
||
ausdrücklich gebeten wurde.
|
||
|
||
**Der Nachweis wird vor dem Senden geschrieben, nicht danach.** Bricht der
|
||
Versand mittendrin ab, gilt die Meldung als zugestellt und kommt nicht
|
||
wieder. Andersherum würde ein hängender Mailserver dieselbe Nachricht alle
|
||
zehn Minuten erneut versuchen. Lieber eine verpasste Nachricht als eine,
|
||
die im Minutentakt klopft.
|
||
|
||
**Was in die Ruhezeit fällt, wird nicht nachgeholt.** Eine Erinnerung an
|
||
einen Zeitblock, der längst begonnen hat, ist nur der Beweis, dass die
|
||
Anwendung zu spät kommt.
|
||
|
||
**Abgleichfehler erst ab dem dritten Versuch in Folge.** Ein Netz hustet,
|
||
ein Anbieter hat Wartung. Beim ersten Fehlschlag zu melden hieße, für
|
||
Dinge zu wecken, die sich von selbst erledigen. Nach dem dritten Mal ist es
|
||
dagegen dringend — solange steht die Wochenansicht auf altem Stand.
|
||
|
||
**Der Tagesüberblick entfällt, wenn es nichts zu sagen gibt.** „Heute steht
|
||
nichts an" ist keine Information, sondern Lärm.
|
||
|
||
**Tote Abos putzen sich selbst weg.** 404 und 410 heißen endgültig, das
|
||
Gerät verschwindet sofort; andere Fehler zählen hoch und führen nach fünf
|
||
Versuchen zum selben Ergebnis. Eine Liste, die von Hand gepflegt werden
|
||
muss, wird nicht gepflegt.
|
||
|
||
**Push-Schlüssel liegen verschlüsselt, Endpunkte nie im Protokoll.** Ein
|
||
Endpunkt genügt, um jemandem Nachrichten zu schicken — er gehört damit in
|
||
dieselbe Klasse wie ein Token.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `pushManager.subscribe` lehnte den VAPID-Schlüssel als `Uint8Array<ArrayBufferLike>` ab | Der Puffer wird jetzt ausdrücklich als `ArrayBuffer` angelegt; ein geteilter Puffer ist dort nicht erlaubt. |
|
||
| Der Integrationstest brach mit einem Fremdschlüsselfehler ab | Die neuen Prüfungen standen hinter dem Löschen des Testbenutzers. Jetzt davor — und danach wird geprüft, dass Geräte und Nachweise mit ihm verschwinden. |
|
||
| E2E-Läufe scheiterten unter Last immer wieder beim Anmelden | Ein Klick, der vor der Hydratation ankommt, verpufft folgenlos. Der Anmeldehelfer liegt jetzt einmal in `e2e/hilfe.ts` und fasst nach, bis die Startseite da ist. |
|
||
| Die Karte meldete gleichzeitig „Browser blockiert" und „Server nicht eingerichtet" | Zwei Aussagen, von denen nur eine zählt. Der Serverzustand geht jetzt vor. Gefunden nur durch das Ansehen des aufgenommenen Bildes. |
|
||
| Das erfundene Push-Gerät in den Demo-Daten wäre beim ersten Versandlauf gelöscht worden | Seine Schlüssel lagen als Klartext-Platzhalter, und unlesbare Schlüssel führen zum Entfernen. Die Demo-Daten enthalten jetzt kein Gerät mehr — die Bilder zeigen den Zustand davor. |
|
||
| Im Bild meldet der Browser „blockiert", obwohl die Erlaubnis erteilt wurde | Chromium im Kopflosbetrieb antwortet auf `Notification.permission` immer mit `denied`; `grantPermissions` ändert daran nichts. Das Handbuch schreibt es jetzt unter das Bild. |
|
||
| Ein Docker-Bau brauchte 14 Minuten, ein E2E-Lauf 18 | Systemlast, kein Codeproblem: dieselben Läufe danach in 1,5 beziehungsweise 0,4 Minuten. Ein vergessener Testserver auf Port 3100 hat kräftig mitgeholfen. |
|
||
|
||
### Offene Punkte
|
||
|
||
- Der Versandweg ist gegen die Logik geprüft, nicht gegen echte Dienste:
|
||
ohne VAPID-Schlüssel und SMTP-Zugang geht lokal nichts hinaus. Das braucht
|
||
einen Lauf auf dem Server.
|
||
- Mehrere Anlässe erscheinen als mehrere Nachrichten. Ob eine Sammelmeldung
|
||
fehlt, zeigt erst der Alltag.
|
||
|
||
### Commits
|
||
|
||
- 24f613d feat: Datenmodell und Umgebung für Benachrichtigungen
|
||
- ddb13d7 feat: Regeln für Benachrichtigungen als reine Logik
|
||
- e0e11c8 feat: Versand per Web-Push und E-Mail
|
||
- b622c1b feat: Benachrichtigungen in den Einstellungen bedienen
|
||
- 047a4a1 feat: Meldungen alle zehn Minuten prüfen und verschicken
|
||
- 5bbbdd5 chore: Demo-Daten und Bildaufnahme für Benachrichtigungen
|
||
- f03ea73 docs: Handbuchseite und README-Abschnitt zu Benachrichtigungen
|
||
- 6e1f279 test: Benachrichtigungen end-to-end und in der Datenbank absichern
|
||
- 7bef435 fix: Serverzustand geht der Browsermeldung vor
|
||
- 35ecb2f chore: Screenshot-Instanz mit eingerichteten Meldewegen
|
||
- bb7b189 docs: Handbuchbilder für M5 neu aufnehmen
|
||
|
||
---
|
||
|
||
## 2026-09-10 – M4: Wochenplanung und Zeitblöcke
|
||
|
||
**Dauer:** 167 Minuten (20:42–23:29), Art `claude`.
|
||
|
||
**Ziel der Sitzung:** Die Kernidee des Projekts — Aufgaben per Ziehen und
|
||
Ablegen in echte Kalendertermine verwandeln, mit Rückweg.
|
||
|
||
### Erledigt
|
||
|
||
- `Zeitblock` samt Migration und Schleifenschutz (30 Tests)
|
||
- Schreibweg zu Graph und Google mit `If-Match`, Rückweg im Abgleich
|
||
- Rasterlogik für die Wochenansicht (21 Tests)
|
||
- Wochenansicht mit `@dnd-kit/core`: einplanen, verschieben, ausplanen
|
||
- Kapazität und Belegung je Wochentag
|
||
- 3 neue E2E-Tests, Handbuchseite
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Der Fingerabdruck geht nur über Beginn und Ende.** Titel und Beschreibung
|
||
ändern sich auch aus harmlosen Gründen — etwa weil der Aufgabentitel
|
||
bearbeitet wurde. Für die Planung zählt allein, wann der Block liegt; alles
|
||
andere in den Fingerabdruck zu nehmen hieße, den Anbieter grundlos zu
|
||
behelligen.
|
||
|
||
**Der Kalender gewinnt, ausnahmslos.** Wer im Kalender etwas verschiebt,
|
||
meint es so. Säße der Planer am längeren Hebel, zöge er dem Benutzer den
|
||
Termin wieder weg — und der wüsste nicht, warum. Bei HTTP 412 verwirft der
|
||
Planer sogar seinen eigenen Fingerabdruck, damit der nächste Abgleich die
|
||
fremde Änderung sicher als solche erkennt.
|
||
|
||
**Scheitert das Schreiben, überlebt die Planung.** Der Zeitblock bleibt mit
|
||
vermerktem Fehler stehen. Ihn zu verwerfen hieße, die Planung des Benutzers
|
||
wegen eines Netzproblems zu vergessen; das nächste Verschieben trägt ihn
|
||
nach. Beim Löschen ist es umgekehrt: Da verschwindet der Zeitblock auch bei
|
||
unerreichbarem Kalender, sonst wäre die Planung blockiert.
|
||
|
||
**Die Dauer kommt aus dem geschätzten Aufwand.** Wer schätzt, bekommt die
|
||
passende Blockgröße geschenkt. Das belohnt genau die Gewohnheit, die die
|
||
Planung überhaupt erst tragfähig macht.
|
||
|
||
**Ziehen beginnt erst nach acht Pixeln.** Ohne diese Schwelle löst jeder
|
||
Klick einen Zug aus und die Knöpfe im Block wären nicht mehr bedienbar.
|
||
|
||
**Überbuchte Tage werden rot, aber nicht gesperrt.** Der Planer ist ein
|
||
Werkzeug, kein Aufpasser. Manchmal muss ein Tag überbucht sein.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||
| Ein Zeitblock steht auch im Terminspiegel und erschien doppelt — einmal als eigene Planung, einmal als fremder Termin | Die Wochenabfrage filtert Termine heraus, deren `extId` zu einem Zeitblock gehört. |
|
||
| Ein Build lief 32 Minuten statt Sekunden | Systemlast durch einen gleichzeitigen Docker-Bau. Ein sauberer Neubau danach: 46 Sekunden. Kein Codeproblem. |
|
||
| Sich überschneidende Termine lagen unsichtbar übereinander | `spurenVerteilen` legt sie nebeneinander und gibt eine Spur wieder frei, sobald sie zu Ende ist. |
|
||
|
||
### Offene Punkte
|
||
|
||
- **Der Schreibweg ist nicht gegen die echten APIs geprüft.** Wie in M3
|
||
fehlen die Zugangsdaten. Die Logik ist getestet, der erste echte Schreib-
|
||
und Rückweg ist Florians.
|
||
- Die Dauer lässt sich nicht am Balken ziehen; wer eine andere will, ändert
|
||
den Aufwand der Aufgabe.
|
||
- Ein Zeitblock landet immer im ersten verbundenen Kalender. Das
|
||
Standard-Kalenderkonto je Bereich ist im Datenmodell vorbereitet, aber
|
||
noch nicht in der Oberfläche.
|
||
|
||
### Commits
|
||
|
||
- 5791047 feat(zeitbloecke): Datenmodell und Schleifenschutz
|
||
- 9c1c916 feat(zeitbloecke): Schreibweg in den Kalender und Rückweg zurück
|
||
- bcb4b8e feat(woche): Rasterlogik für die Wochenansicht
|
||
- fada445 feat(woche): Wochenansicht mit Ziehen und Ablegen
|
||
- 5b1e998 test: Wochenansicht und Ausplanen absichern
|
||
- 0974695 docs: Handbuchseite zur Wochenplanung
|
||
|
||
---
|
||
|
||
## 2026-09-10 – M3: Kalender lesen
|
||
|
||
**Dauer:** 41 Minuten (17:48–18:29), Art `claude`.
|
||
|
||
**Ziel der Sitzung:** Termine aus Microsoft 365 und Google lesen, damit der
|
||
Planer weiß, wann überhaupt Platz ist.
|
||
|
||
### Erledigt
|
||
|
||
- `Kalenderkonto` und `Termin` samt Migration; Bereich bekommt sein
|
||
Standard-Kalenderkonto für M4
|
||
- Tokenverschlüsselung mit AES-256-GCM
|
||
- Antworten beider Anbieter mit zod geprüft und in eine gemeinsame Form
|
||
gebracht, geprüft gegen anonymisierte Beispielantworten
|
||
- Belegungsrechnung: Kapazität gegen Termine, freie Fenster für M4
|
||
- OAuth-Verbinden, Tokenerneuerung, seitenweiser Abgleich mit Rückfall auf
|
||
einen vollständigen Durchlauf
|
||
- Seite für die Kalenderkonten, Belegung des Tages auf der Startseite
|
||
- Hintergrundjobs für Abgleich und nächtliches Aufräumen
|
||
- 68 neue Unit-Tests, 10 neue Integrationsprüfungen
|
||
- Handbuchseite und Einrichtungsanleitung für Entra ID und Google
|
||
|
||
### Entscheidungen mit Begründung
|
||
|
||
**Tokens verschlüsselt, Schlüssel aus AUTH_SECRET abgeleitet.** Wer die
|
||
Datenbank oder eine Sicherung bekommt, hätte sonst direkten Zugriff auf
|
||
fremde Kalender — das wiegt schwerer als bei den OIDC-Tokens aus M1, die
|
||
nur die Anmeldung betreffen. Eine eigene Pflichtvariable wäre eine weitere
|
||
Stelle, an der die Einrichtung scheitert; die Ableitung über HKDF kostet
|
||
nichts und hat die richtige Nebenwirkung: wer AUTH_SECRET wechselt, muss
|
||
die Konten neu verbinden — dann sind ohnehin alle Sitzungen ungültig.
|
||
|
||
**Bei fremder Zeitzone abbrechen statt raten.** Graph liefert Datum und
|
||
Zeitzone getrennt, ohne Versatz im Text. Wir fragen ausdrücklich UTC an;
|
||
kommt etwas anderes zurück, wäre jede Umrechnung geraten und würde Termine
|
||
um Stunden verschieben. Ein Abbruch mit klarer Meldung ist besser als ein
|
||
Kalender, der leise falsch liegt.
|
||
|
||
**Unbekanntes showAs zählt als belegt.** Lieber eine Stunde zu viel
|
||
blockiert als eine Doppelbuchung.
|
||
|
||
**Schreibrechte schon jetzt anfragen.** M3 liest nur, aber M4 schreibt.
|
||
Später erneut zuzustimmen ist genau der Schritt, an dem Einrichtung
|
||
scheitert — einmal breiter fragen ist das kleinere Übel.
|
||
|
||
**Ganztägige Termine zählen nicht zur Belegung.** Ein Urlaubstag oder
|
||
Geburtstag blockiert sonst den ganzen Tag, obwohl er keine Arbeitszeit
|
||
kostet.
|
||
|
||
**Alle Kalender zählen gegen jeden Bereich.** Ein Termin blockiert die Zeit
|
||
ohnehin, gleich zu welchem Lebensbereich er gehört.
|
||
|
||
**Kein SDK für Graph und Google.** Es geht um drei Aufrufe je Anbieter, und
|
||
die Antworten werden ohnehin mit zod geprüft. Ein SDK brächte vor allem
|
||
Abhängigkeiten und blinde Flecken.
|
||
|
||
### Probleme und Lösungen
|
||
|
||
| Problem | Lösung |
|
||
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Prisma lässt in einem zusammengesetzten Unique-Schlüssel kein null zu | `kalenderId` ist nicht optional, sondern trägt den sprechenden Wert `"standard"` für den Standardkalender. |
|
||
| Googles `syncToken` verträgt sich nicht mit `timeMin`/`timeMax` | Folgeabgleiche liefern auch Änderungen außerhalb des Fensters. Was daneben liegt, wird beim Spiegeln verworfen — sonst wüchse der Spiegel unbegrenzt. |
|
||
| Der Verweis für die nächste Runde kann ablaufen (HTTP 410) | Verweis verwerfen, Spiegel leeren, von vorn lesen. Der Ablauf ist so gebaut, dass ein vollständiger Durchlauf jederzeit möglich ist. |
|
||
| Die Shell zerlegte in mehreren Anläufen Template-Literale und Backticks | Auf `node - <<'SKRIPT'` umgestellt; damit geht der Text unverändert durch. |
|
||
|
||
### Offene Punkte
|
||
|
||
- **Nicht gegen die echten APIs getestet.** Ich habe keine Zugangsdaten für
|
||
Entra ID oder Google. Provider und Abgleich sind gegen anonymisierte
|
||
Beispielantworten geprüft; den ersten echten Verbindungsversuch muss
|
||
Florian machen. Die Anleitung dafür steht in der README.
|
||
- Nur der Standardkalender je Konto. Mehrere Kalender desselben Kontos sind
|
||
im Datenmodell vorgesehen, es fehlt die Auswahl in der Oberfläche.
|
||
- Der Abgleich läuft in einer Schleife über alle Konten. Bei vielen Konten
|
||
wäre das nacheinander zu langsam; bei der erwarteten Zahl ist es das
|
||
einfachere Verhalten.
|
||
|
||
### Commits
|
||
|
||
- 79e6c0d feat(kalender): Datenmodell für Kalenderkonten und Termine
|
||
- 6fda2a5 feat(kalender): Antworten von Graph und Google lesen, Belegung rechnen
|
||
- 9946e7c feat(kalender): OAuth und Abgleich für Graph und Google
|
||
- 0c67b73 feat(kalender): Seite für die Kalenderkonten
|
||
- 42c98c8 feat(kalender): Hintergrundjobs für Abgleich und Aufräumen
|
||
- d8d3227 feat(kalender): Belegung des Tages auf der Startseite
|
||
- df50627 test: Verschlüsselung und Kalendermodelle absichern
|
||
- a8b2ff0 docs: Handbuchseite und Einrichtung der Kalenderanbieter
|
||
|
||
---
|
||
|
||
## 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
|