npm run lint war da, aber es gab nichts zu prüfen. Ein Werkzeug, das aussieht, als prüfe es etwas, und nichts prüft, ist schlechter als gar keines: es steht in der Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das für ein Ergebnis.
Jetzt Flat Config (eslint.config.mjs), ohne Formatierungsregeln — die macht Prettier, und zwei Werkzeuge, die sich über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt. Auch ohne typgestützte Regeln: no-floating-promises wäre für diesen Stack die wertvollste Regel überhaupt, braucht aber den Compiler bei jedem Lauf. Bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch.
next lint ist in Next 15.5 abgekündigt; das Skript ruft jetzt eslint . auf, dazu npm run lint:fix.
Er hat gleich zwei Fehler gefunden, die niemand gesucht hatte:
Die Fehlerfläche der Karte konnte nie etwas anzeigen — der Zustand dahinter wurde nirgends gesetzt. Eine gescheiterte Abfrage sah damit aus wie „keine Hydranten vorhanden". Die Meldung kommt jetzt aus den Abfragen selbst.
Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu. Ein ?? [] erzeugte jedes Mal ein neues leeres Feld. Bei einer Karte mit dreißig Elementen und einem Ereignisstrom im Zwei-Sekunden-Takt kein akademischer Punkt.
Dazu Kleinkram: unbenutzte Importe in fünf Dateien, vier deutsche Anführungszeichen, die mit einem geraden Zoll-Zeichen schlossen, und drei eslint-disable-Zeilen, die nichts mehr unterdrückten.
Die Meldung stand seit Phase 3 in der Konsole und ist der klassische Auslöser für einen Hydration-Fehler: der Browser schließt den Absatz vor dem <div>, baut also einen anderen Baum als React erwartet.
Die Meldung kam von einer Stelle, die Ursache saß im Bauteil: Badge rendert jetzt ein <span> statt eines <div>. Ein Abzeichen ist Fließtext-Inhalt und gehört in ein Inline-Element — so ist auch jede künftige Verwendung in einem Absatz richtig. Die Darstellung ändert sich nicht, inline-flex verhält sich in beiden gleich.
Gegengeprüft mit einem Konsolen-Mitschnitt der geöffneten Einsatzseite: keine Verschachtelungsmeldung mehr.
Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht war. Bei jedem Lauf unterschieden sich alle 72 Bildschirmfotos — nicht weil sich die Oberfläche geändert hätte, sondern weil andere Zahlen in den Listen standen.
Jetzt eine zweite Wehr.npm run db:test-reset löscht ff-teststelle und legt sie neu an, mit denselben Beispieldaten. Der Seed konnte das ohnehin schon — er brauchte nur seine Kennung aus der Umgebung.
Gewählt wurde Löschen-und-neu-Anlegen statt Aufräumen-danach: ein abgebrochener Lauf lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger Neuaufbau ist der einzige Zustand, den man kennt.
Ein Riegel im Skript bricht ab, wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert.
Dazu tests/e2e/wehr.ts: Zugangsdaten und Anmeldung an einer Stelle statt achtmal wortgleich in acht Dateien. Dass die Zugangsdaten an neun Stellen zu ändern waren, war der eigentliche Grund, warum diese Umstellung so lange liegen geblieben ist.
Zwei Tests fielen nach der Umstellung durch — und das war richtig so
Sie hatten die Verschmutzung gebraucht:
getByText("B 3 Wohnhausbrand", { exact: true }) traf bisher einen Einsatz aus einem früheren Testlauf, weil der kein Objekt hatte. Der Einsatz aus dem Seed hat eines, und beides steht in derselben Zelle.
Der Kiosk-Test hatte const WEHR = "ff-musterhausen" fest eingetragen. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin — das war der letzte verbliebene Weg in die Beispielwehr.
Die Bildschirmfotos sind gegen den sauberen Bestand neu erzeugt. Nebenbei entsteht das Bild der Atemschutzüberwachung wieder, das seit einigen Phasen übersprungen wurde — in der verschmutzten Wehr gab es keine laufende Überwachung mehr zu finden.
Nebenbefund
tests/e2e war in der tsconfig.json vom Typecheck ausgenommen. Die Ausnahme hat beim Umbau einen doppelt deklarierten Bezeichner durchgelassen, den erst der Testlauf gemeldet hat — npx tsc --noEmit hatte geschwiegen. Die Dateien übersetzen sauber; die Ausnahme ist weg.
Nachweise
npx tsc --noEmit — ohne Ausgabe, jetzt einschließlichtests/e2e
npm run lint — ohne Beanstandung
npm run test — 495 Tests grün
npx playwright test — 213 grün, 0 fehlgeschlagen, 3 flaky (bekannte Zeitlimits auf dem Tablet-Profil)
npm run build — sauber
Konsolenmitschnitt der Einsatzseite: keine DOM-Verschachtelung mehr
Zur Regel in der CLAUDE.md
Ich habe für diese Arbeit drei Issues angelegt, obwohl dort steht „Niemals ein Issue anlegen". Das geschah auf deine ausdrückliche Anweisung im Chat. Die Statuslabels in-arbeit, zu-testen und blockiert gab es im Repository noch nicht — die sind einmalig angelegt, wie es die Regel für diesen einen Fall erlaubt.
Die drei Punkte, die seit mehreren Phasen im Abschlussbericht standen.
Closes #10 · Closes #11 · Closes #12
---
## #11 — ESLint hatte keine Konfiguration
`npm run lint` war da, aber es gab nichts zu prüfen. Ein Werkzeug, das aussieht, als prüfe es etwas, und nichts prüft, ist schlechter als gar keines: es steht in der Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das für ein Ergebnis.
Jetzt Flat Config (`eslint.config.mjs`), **ohne Formatierungsregeln** — die macht Prettier, und zwei Werkzeuge, die sich über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt. Auch **ohne typgestützte Regeln**: `no-floating-promises` wäre für diesen Stack die wertvollste Regel überhaupt, braucht aber den Compiler bei jedem Lauf. Bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch.
`next lint` ist in Next 15.5 abgekündigt; das Skript ruft jetzt `eslint .` auf, dazu `npm run lint:fix`.
**Er hat gleich zwei Fehler gefunden, die niemand gesucht hatte:**
- **Die Fehlerfläche der Karte konnte nie etwas anzeigen** — der Zustand dahinter wurde nirgends gesetzt. Eine gescheiterte Abfrage sah damit aus wie „keine Hydranten vorhanden". Die Meldung kommt jetzt aus den Abfragen selbst.
- **Zwei `useMemo` der Lagekarte rechneten bei jedem Rendern neu.** Ein `?? []` erzeugte jedes Mal ein neues leeres Feld. Bei einer Karte mit dreißig Elementen und einem Ereignisstrom im Zwei-Sekunden-Takt kein akademischer Punkt.
Dazu Kleinkram: unbenutzte Importe in fünf Dateien, vier deutsche Anführungszeichen, die mit einem geraden Zoll-Zeichen schlossen, und drei `eslint-disable`-Zeilen, die nichts mehr unterdrückten.
## #12 — `<div>` in einem `<p>` auf der Einsatzseite
Die Meldung stand seit Phase 3 in der Konsole und ist der klassische Auslöser für einen Hydration-Fehler: der Browser schließt den Absatz vor dem `<div>`, baut also einen anderen Baum als React erwartet.
Die Meldung kam von einer Stelle, **die Ursache saß im Bauteil**: `Badge` rendert jetzt ein `<span>` statt eines `<div>`. Ein Abzeichen ist Fließtext-Inhalt und gehört in ein Inline-Element — so ist auch jede künftige Verwendung in einem Absatz richtig. Die Darstellung ändert sich nicht, `inline-flex` verhält sich in beiden gleich.
Gegengeprüft mit einem Konsolen-Mitschnitt der geöffneten Einsatzseite: keine Verschachtelungsmeldung mehr.
## #10 — Die E2E-Tests schrieben in die Beispielwehr
Der Stand vorher:
| | Beispielwehr | laut Seed |
|---|---:|---:|
| Geräte | 115 | 45 |
| Einsätze | 39 | 8 |
| Atemschutz-Überwachungen | 120 | 1 |
| Wiki-Seiten | 18 | 4 |
Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht war. Bei jedem Lauf unterschieden sich alle 72 Bildschirmfotos — nicht weil sich die Oberfläche geändert hätte, sondern weil andere Zahlen in den Listen standen.
**Jetzt eine zweite Wehr.** `npm run db:test-reset` löscht `ff-teststelle` und legt sie neu an, mit denselben Beispieldaten. Der Seed konnte das ohnehin schon — er brauchte nur seine Kennung aus der Umgebung.
Gewählt wurde Löschen-und-neu-Anlegen statt Aufräumen-danach: ein abgebrochener Lauf lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger Neuaufbau ist der einzige Zustand, den man kennt.
Ein Riegel im Skript bricht ab, wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert.
Dazu `tests/e2e/wehr.ts`: Zugangsdaten und Anmeldung an **einer** Stelle statt achtmal wortgleich in acht Dateien. Dass die Zugangsdaten an neun Stellen zu ändern waren, war der eigentliche Grund, warum diese Umstellung so lange liegen geblieben ist.
### Zwei Tests fielen nach der Umstellung durch — und das war richtig so
Sie hatten die Verschmutzung gebraucht:
- `getByText("B 3 Wohnhausbrand", { exact: true })` traf bisher einen Einsatz aus einem früheren Testlauf, weil der **kein Objekt** hatte. Der Einsatz aus dem Seed hat eines, und beides steht in derselben Zelle.
- Der Kiosk-Test hatte `const WEHR = "ff-musterhausen"` fest eingetragen. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin — das war der letzte verbliebene Weg in die Beispielwehr.
### Der Beweis
Nach einem vollständigen Testlauf:
```
slug | geraete | einsaetze | atemschutz | termine | wiki | maengel
-----------------+---------+-----------+------------+---------+------+---------
ff-musterhausen | 49 | 8 | 1 | 19 | 4 | 5 ← Seed-Stand
ff-teststelle | 53 | 10 | 13 | 23 | 10 | 3 ← gewachsen
```
Die Bildschirmfotos sind gegen den sauberen Bestand neu erzeugt. Nebenbei entsteht das Bild der Atemschutzüberwachung wieder, das seit einigen Phasen übersprungen wurde — in der verschmutzten Wehr gab es keine laufende Überwachung mehr zu finden.
## Nebenbefund
**`tests/e2e` war in der `tsconfig.json` vom Typecheck ausgenommen.** Die Ausnahme hat beim Umbau einen doppelt deklarierten Bezeichner durchgelassen, den erst der Testlauf gemeldet hat — `npx tsc --noEmit` hatte geschwiegen. Die Dateien übersetzen sauber; die Ausnahme ist weg.
## Nachweise
- `npx tsc --noEmit` — ohne Ausgabe, jetzt **einschließlich** `tests/e2e`
- `npm run lint` — ohne Beanstandung
- `npm run test` — 495 Tests grün
- `npx playwright test` — **213 grün, 0 fehlgeschlagen**, 3 flaky (bekannte Zeitlimits auf dem Tablet-Profil)
- `npm run build` — sauber
- Konsolenmitschnitt der Einsatzseite: keine DOM-Verschachtelung mehr
## Zur Regel in der CLAUDE.md
Ich habe für diese Arbeit drei Issues angelegt, obwohl dort steht „Niemals ein Issue anlegen". Das geschah auf deine ausdrückliche Anweisung im Chat. Die Statuslabels `in-arbeit`, `zu-testen` und `blockiert` gab es im Repository noch nicht — die sind einmalig angelegt, wie es die Regel für diesen einen Fall erlaubt.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
ESLint hatte keine Konfiguration — das Skript "npm run lint" pruefte nichts.
Jetzt Flat Config ohne Formatierungsregeln, dafuer mit den Regeln, die die
Fehler dieses Projekts gemeldet haetten. Er hat gleich zwei gefunden, die
niemand gesucht hatte.
Das Badge rendert ein span statt eines div. Damit ist die Meldung
"div cannot be a descendant of p" weg, die seit Phase 3 auf der Einsatzseite
stand — sie ist der klassische Ausloeser fuer einen Hydration-Fehler.
Die E2E-Tests laufen gegen eine eigene Wehr. Nach fuenf Phasen standen in der
Beispielwehr 120 Atemschutz-Ueberwachungen statt einer und 115 Geraete statt
45; bei jedem Lauf aenderten sich alle Bildschirmfotos. Der Bestand ist einmal
zurueckgesetzt, npm run db:test-reset baut die Testwehr neu auf.
Behoben nebenbei:
- Die Fehlerflaeche der Karte konnte nie etwas anzeigen.
- Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu.
- tests/e2e war vom Typecheck ausgenommen und liess einen doppelt
deklarierten Bezeichner durch.
Closes#10Closes#11Closes#12
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Die drei Punkte, die seit mehreren Phasen im Abschlussbericht standen.
Closes #10 · Closes #11 · Closes #12
#11 — ESLint hatte keine Konfiguration
npm run lintwar da, aber es gab nichts zu prüfen. Ein Werkzeug, das aussieht, als prüfe es etwas, und nichts prüft, ist schlechter als gar keines: es steht in der Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das für ein Ergebnis.Jetzt Flat Config (
eslint.config.mjs), ohne Formatierungsregeln — die macht Prettier, und zwei Werkzeuge, die sich über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt. Auch ohne typgestützte Regeln:no-floating-promiseswäre für diesen Stack die wertvollste Regel überhaupt, braucht aber den Compiler bei jedem Lauf. Bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch.next lintist in Next 15.5 abgekündigt; das Skript ruft jetzteslint .auf, dazunpm run lint:fix.Er hat gleich zwei Fehler gefunden, die niemand gesucht hatte:
useMemoder Lagekarte rechneten bei jedem Rendern neu. Ein?? []erzeugte jedes Mal ein neues leeres Feld. Bei einer Karte mit dreißig Elementen und einem Ereignisstrom im Zwei-Sekunden-Takt kein akademischer Punkt.Dazu Kleinkram: unbenutzte Importe in fünf Dateien, vier deutsche Anführungszeichen, die mit einem geraden Zoll-Zeichen schlossen, und drei
eslint-disable-Zeilen, die nichts mehr unterdrückten.#12 —
<div>in einem<p>auf der EinsatzseiteDie Meldung stand seit Phase 3 in der Konsole und ist der klassische Auslöser für einen Hydration-Fehler: der Browser schließt den Absatz vor dem
<div>, baut also einen anderen Baum als React erwartet.Die Meldung kam von einer Stelle, die Ursache saß im Bauteil:
Badgerendert jetzt ein<span>statt eines<div>. Ein Abzeichen ist Fließtext-Inhalt und gehört in ein Inline-Element — so ist auch jede künftige Verwendung in einem Absatz richtig. Die Darstellung ändert sich nicht,inline-flexverhält sich in beiden gleich.Gegengeprüft mit einem Konsolen-Mitschnitt der geöffneten Einsatzseite: keine Verschachtelungsmeldung mehr.
#10 — Die E2E-Tests schrieben in die Beispielwehr
Der Stand vorher:
Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht war. Bei jedem Lauf unterschieden sich alle 72 Bildschirmfotos — nicht weil sich die Oberfläche geändert hätte, sondern weil andere Zahlen in den Listen standen.
Jetzt eine zweite Wehr.
npm run db:test-resetlöschtff-teststelleund legt sie neu an, mit denselben Beispieldaten. Der Seed konnte das ohnehin schon — er brauchte nur seine Kennung aus der Umgebung.Gewählt wurde Löschen-und-neu-Anlegen statt Aufräumen-danach: ein abgebrochener Lauf lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger Neuaufbau ist der einzige Zustand, den man kennt.
Ein Riegel im Skript bricht ab, wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert.
Dazu
tests/e2e/wehr.ts: Zugangsdaten und Anmeldung an einer Stelle statt achtmal wortgleich in acht Dateien. Dass die Zugangsdaten an neun Stellen zu ändern waren, war der eigentliche Grund, warum diese Umstellung so lange liegen geblieben ist.Zwei Tests fielen nach der Umstellung durch — und das war richtig so
Sie hatten die Verschmutzung gebraucht:
getByText("B 3 Wohnhausbrand", { exact: true })traf bisher einen Einsatz aus einem früheren Testlauf, weil der kein Objekt hatte. Der Einsatz aus dem Seed hat eines, und beides steht in derselben Zelle.const WEHR = "ff-musterhausen"fest eingetragen. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin — das war der letzte verbliebene Weg in die Beispielwehr.Der Beweis
Nach einem vollständigen Testlauf:
Die Bildschirmfotos sind gegen den sauberen Bestand neu erzeugt. Nebenbei entsteht das Bild der Atemschutzüberwachung wieder, das seit einigen Phasen übersprungen wurde — in der verschmutzten Wehr gab es keine laufende Überwachung mehr zu finden.
Nebenbefund
tests/e2ewar in dertsconfig.jsonvom Typecheck ausgenommen. Die Ausnahme hat beim Umbau einen doppelt deklarierten Bezeichner durchgelassen, den erst der Testlauf gemeldet hat —npx tsc --noEmithatte geschwiegen. Die Dateien übersetzen sauber; die Ausnahme ist weg.Nachweise
npx tsc --noEmit— ohne Ausgabe, jetzt einschließlichtests/e2enpm run lint— ohne Beanstandungnpm run test— 495 Tests grünnpx playwright test— 213 grün, 0 fehlgeschlagen, 3 flaky (bekannte Zeitlimits auf dem Tablet-Profil)npm run build— sauberZur Regel in der CLAUDE.md
Ich habe für diese Arbeit drei Issues angelegt, obwohl dort steht „Niemals ein Issue anlegen". Das geschah auf deine ausdrückliche Anweisung im Chat. Die Statuslabels
in-arbeit,zu-testenundblockiertgab es im Repository noch nicht — die sind einmalig angelegt, wie es die Regel für diesen einen Fall erlaubt.🤖 Generated with Claude Code