Files
tessera-ctl/.planning/quick/260921-gof-effekt-abhaengigkeiten-in-react-21-befun/260921-gof-PLAN.md
T
schalli 54fdf699dc docs(quick-260921-gof): Plan fuer die 21 Effekt-Abhaengigkeiten in React
Alle 21 Befunde der Regel useExhaustiveDependencies einzeln beurteilt und
in vier Klassen eingeteilt: 2x Defekt, 15x Falle, 3x Absicht, 1x Ballast.
Die Vorgabe kennt nur A/B/C -- Biome meldet aber auch ueberfluessige
Abhaengigkeiten, deshalb die vierte Klasse fuer reinen Ballast.

Acht Befunde sind dieselbe t-Falle aus dem Vorgang 260921-bi2: die
Testattrappen fuer next-intl liefern bei jedem Durchlauf eine frische
Funktion, t in eine Abhaengigkeitsliste einzutragen ist hier belegbar
eine Abruf-Schleife. Einheitlicher Griff: uebersetzten Text vor dem Hook
als Zeichenkette festhalten (Wertvergleich statt Identitaetsvergleich).

Kalender: showToday muss zuerst identitaetserhaltend werden, sonst loest
der naive Griff bei jedem Druck auf den Monatsknopf einen Termin-Abruf
bis zum Exchange-Server aus. Nachweis am laufenden System ueber das
Netzwerkprotokoll des Browsers, nicht per fetch aus der Seite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
2026-09-21 12:15:40 +02:00

44 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, estimate, must_haves
phase plan type wave depends_on files_modified autonomous requirements estimate must_haves
quick-260921-gof 01 execute 1
apps/web/src/components/dashboard/widgets/calendar-widget.tsx
apps/web/src/components/dashboard/widgets/calendar-widget.test.tsx
apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx
apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx
apps/web/src/components/dashboard/widgets/favorites-widget.tsx
apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx
apps/web/src/components/layout/sidebar.tsx
apps/web/src/components/layout/sidebar.test.tsx
apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.tsx
apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.test.tsx
apps/web/src/app/(portal)/admin/modules/components/ActivateModuleDialog.tsx
apps/web/src/app/(portal)/admin/modules/grants/page.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx
apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.tsx
apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.test.tsx
true
D-01
D-02
D-03
D-04
D-05
D-06
D-07
tokens raw_tokens tasks confidence
115000 115000 3 low
truths artifacts key_links
Nach dem Umbau meldet Biome fuer die Regel useExhaustiveDependencies keinen einzigen Befund mehr: weder einen offenen noch einen unbegruendet stillgelegten (D-01).
Jede der drei stehengelassenen Abhaengigkeiten traegt ein biome-ignore mit deutschem Grund im Quelltext; es gibt keine stille Unterdrueckung (D-01, D-02).
Das Kalender-Widget holt im Browser-Netzwerkprotokoll ueber 60 Sekunden genau einen Termin-Abruf, pro Monatswechsel genau einen weiteren und beim Druck auf den Monatsknopf im laufenden Monat keinen zusaetzlichen (D-03, D-04).
Die Stoppuhr laeuft ueber Start, Runde, Stopp und Reset weiter, ohne zu springen, ohne doppelte Geschwindigkeit und ohne Ruecksetzer; pro Klick geht genau ein PATCH an die API (D-03, D-05).
Die Auffrisch-Ausloeser bleiben wirksam: ein Bump von sidebarRefreshKey, ein Klick auf Jetzt pruefen in DKV und ein Klick auf Jetzt abrufen im Ausschreibungsradar loesen je genau einen zusaetzlichen Abruf aus (D-03).
pnpm lint bleibt 5/5 ohne Befund der Schwere error, der Gesamtstand sinkt von 467 auf 446 Meldungen und steigt an keiner anderen Stelle (D-07).
Alle bestehenden Tests bleiben gruen und die Zahl der Testdateien und Tests in apps/web und apps/api sinkt nicht (D-03).
Es kommt kein Paket hinzu, keine Version wird angehoben und keine Datei ausserhalb der 15 Befund-Dateien und ihrer Tests wird angefasst (D-06).
apps/web/src/components/dashboard/widgets/calendar-widget.tsx — ohne ueberfluessiges useMemo, Ladeeffekt an monthDate statt an monthDate.getTime()
apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx — Takt-Effekt greift nur noch auf Einzelwerte zu, nicht mehr auf das ganze sw-Objekt
apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx — biome-ignore mit deutschem Grund fuer refreshKey
apps/web/src/components/layout/sidebar.tsx — biome-ignore mit deutschem Grund fuer sidebarRefreshKey
apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.test.tsx — neue Abruf-Zaehlprobe
.planning/quick/260921-gof-effekt-abhaengigkeiten-in-react-21-befun/260921-gof-SUMMARY.md — Tabelle mit allen 21 Befunden, Kategorie und Begruendung
useTranslations -> t -> Abhaengigkeitsliste: t wird nirgends mehr in einer Liste gefuehrt, stattdessen wird der uebersetzte Text vor dem Effekt in eine Zeichenkette gelegt (Zeichenketten vergleicht React per Wert, Funktionen per Identitaet)
monthDate -> Ladeeffekt -> fetchEvents -> API -> Exchange/EWS: die Kette darf pro Monatswechsel genau einmal auslaufen
Marketplace-Store sidebarRefreshKey -> Sidebar-Effekt -> GET /modules/active: bleibt bestehen, sonst zeigt die Seitenleiste nach einer Modul-Aktivierung den alten Stand
Eltern-Zaehler refreshKey -> InvoiceHistoryTable / ResultsList -> Neuladen nach Jetzt pruefen bzw. Jetzt abrufen
Alle 21 Befunde der Biome-Regel `lint/correctness/useExhaustiveDependencies` in `apps/web` einzeln beurteilen und beheben — kein Sammel-Fix, kein Sammel-Ignorieren.

Zweck: Diese Regel wurde beim Lint-Rueckbau am 21.09. bewusst ausgespart, weil hinter jedem Befund ein echter React-Fehler stecken kann. Beim Schreiben der Tests im Vorgang 260921-bi2 ist genau so einer aufgetaucht: ein instabiles t aus useTranslations in einer Abhaengigkeitsliste hat eine Abruf-Schleife gegen den Server ausgeloest. Diese Klasse wird hier abgearbeitet.

Ergebnis: 15 Quelldateien bereinigt, drei bewusst stehengelassene Abhaengigkeiten im Quelltext begruendet, rund zehn neue Zaehlproben in den Tests, die einen Rueckfall sofort sichtbar machen, und ein Nachweis am laufenden System fuer die drei gefaehrlichen Ecken (Kalender, Stoppuhr, Auffrisch-Ausloeser).

Ausgangsmessung (21.09.2026, selbst nachgemessen)

npx biome lint . --reporter=json --max-diagnostics=20000 meldet 467 Diagnosen (466 Warnungen + 1 Info), davon 21 useExhaustiveDependencies, 0 der Schwere error. Die 21 Befunde verteilen sich auf 16 Hook-Stellen in 15 Dateien — mehrere Befunde koennen auf derselben Hook-Zeile liegen.

Die vier Klassen

Die Vorgabe kennt drei Kategorien. Biome meldet aber zwei Richtungen: fehlende UND ueberfluessige Abhaengigkeiten. Fuer ueberfluessige, die weder Absicht noch Falle sind, gibt es hier eine vierte Klasse:

  • (A) Defekt — der Effekt liest einen Wert, der sich aendern kann, und laeuft nicht neu. Abhaengigkeit ergaenzen.
  • (B) Falle — die Abhaengigkeit unveraendert zu ergaenzen erzeugt eine Schleife oder macht eine Merkung wirkungslos, weil der Wert bei jedem Durchlauf neu entsteht. Identitaet stabilisieren statt ergaenzen.
  • (C) Absicht — die Abhaengigkeit ist ein Auffrisch-Ausloeser oder der Effekt soll genau einmal laufen. Stehenlassen und mit biome-ignore auf Deutsch begruenden.
  • (D) Ballast — ueberfluessige Abhaengigkeit ohne jede Wirkung. Entfernen.

Verteilung: 2x (A), 15x (B), 3x (C), 1x (D). Der Anteil (C) liegt bei 14 Prozent und damit deutlich unter der Drittel-Grenze aus D-02.

Befundtabelle — alle 21, verbindlich

# Datei Zeile Befund Kat. Begruendung (was ginge schief) Aufgabe
1 calendar-widget.tsx 76 config fehlt B Die Merkung liest das ganze config-Objekt, die Liste nennt nur drei Felder. config als Ganzes einzutragen macht die Merkung wirkungslos, sobald die Kachel-Umgebung ein frisches Objekt reicht. Die Merkung bringt ohnehin nichts, weil nur drei Zahlen/Schalter herausfallen. 1
2 calendar-widget.tsx 76 config.lookaheadDays zu eng B Gleiche Stelle, Gegenrichtung desselben Problems. 1
3 calendar-widget.tsx 82 monthDate fehlt B Der Ladeeffekt haengt an monthDate.getTime(), einem Funktionsaufruf, den Biome nicht verfolgen kann. monthDate direkt einzutragen loest bei jedem Druck auf den Monatsknopf im laufenden Monat einen neuen Termin-Abruf aus, weil showToday heute immer ein frisches Datum setzt — ueber die API bis zum Exchange-Server. 1
4 calendar-widget.tsx 82 monthDate.getTime() ueberfluessig B Gleiche Stelle, Gegenrichtung. 1
5 stopwatch-widget.tsx 81 sw fehlt B Der Takt-Effekt greift auf das ganze Zustandsobjekt zu, die Liste nennt drei Felder. sw einzutragen baut den 100-ms-Takt bei jeder aufgezeichneten Runde ab und neu auf — ohne Not und mit Taktversatz. 1
6 stopwatch-widget.tsx 81 sw.elapsed zu eng B Gleiche Stelle, Gegenrichtung. 1
7 VehicleTable.tsx 182 t fehlt B t steckt nur in der Fehlermeldung des Ladens. Eingetragen wird load bei jedem Durchlauf neu und der Mount-Effekt daran wird zur Abruf-Schleife — der Kommentar an der Stelle beschreibt genau das. 2
8 ResultsList.tsx 89 t fehlt B Dieselbe Falle, und dies ist die Stelle, an der sie im Vorgang 260921-bi2 tatsaechlich zugeschnappt ist. 2
9 TenderDetail.tsx 96 t fehlt B Dieselbe Falle; getroffen waere der Detail-Abruf beim Oeffnen einer Ausschreibung. 2
10 DigestIntervalForm.tsx 32 t fehlt B Dieselbe Falle; getroffen waere das Laden der Zustell-Einstellung. 2
11 SourceConfigForm.tsx 44 t fehlt B Dieselbe Falle; getroffen waere das Laden der Quellen-Konfiguration. 2
12 RssFeedListForm.tsx 74 loadFeeds fehlt B loadFeeds ist eine gewoehnliche Funktion im Rumpf und entsteht bei jedem Durchlauf neu. Eingetragen ergibt das eine endlose Abruf-Schleife gegen die Feed-Liste. 2
13 SavedSearchBar.tsx 141 load fehlt B Gleiche Bauart wie 12, gleiche Folge. 2
14 favorites-widget.tsx 99 t fehlt B Dieselbe t-Falle; der Kommentar an der Stelle nennt sie bereits. 2
15 ResultsList.tsx 89 refreshKey ueberfluessig C refreshKey ist der Auffrisch-Ausloeser der Elternseite nach Jetzt abrufen. Entfernt man ihn, laedt die Trefferliste nach einem Abruf nicht mehr nach. 2
16 InvoiceHistoryTable.tsx 81 refreshKey ueberfluessig C Gleiches Muster: ohne ihn bleibt die DKV-Historie nach Jetzt pruefen auf dem alten Stand. 3
17 sidebar.tsx 57 sidebarRefreshKey ueberfluessig C Ausloeser aus dem Marketplace-Speicher. Ohne ihn zeigt die Seitenleiste ein frisch aktiviertes Modul erst nach einem Neuladen der Seite — eine Anzeige, die hinter dem Berechtigungsstand zurueckbleibt. 3
18 ActivateModuleDialog.tsx 47 moduleId ueberfluessig D Der Effekt holt /groups, was vom Modul nicht abhaengt. Die Elternseite haengt den Dialog je Modul frisch ein und open ist dort fest true — moduleId kann sich waehrend der Lebenszeit gar nicht aendern. Reiner Ballast. 3
19 GroupMembersModal.tsx 89 fetchMembers fehlt A Beide Funktionen sind bereits stabil gehalten; fetchMembers wechselt nur mit group.id. Fehlt sie, zeigt der Dialog bei einem Gruppenwechsel ohne Neuaufbau die Mitglieder der vorigen Gruppe — heute ueber die Oberflaeche nicht erreichbar, aber eine Zeile Umbau davon entfernt. 3
20 GroupMembersModal.tsx 89 fetchAllUsers fehlt A Gleiche Stelle, gleiche Begruendung. 3
21 grants/page.tsx 140 matches fehlt B matches ist eine Pfeilfunktion im Rumpf und entsteht bei jedem Durchlauf neu. Eingetragen laeuft die Filter-Merkung bei jedem Durchlauf neu und ist damit wirkungslos — keine Schleife, aber genau die Merkung weg, wegen der die Stelle gebaut wurde. 3

Der durchgaengige Griff gegen die t-Falle

Acht Befunde sind dieselbe Sache: t aus useTranslations wird ausschliesslich fuer einen Ersatz-Fehlertext innerhalb eines Effekts oder Rueckrufs benutzt. In diesem Projekt ist belegt, dass t nicht als stabil angenommen werden darf: die Testattrappen in apps/web (zum Beispiel in ResultsList.test.tsx und calendar-widget.test.tsx) liefern bei jedem Durchlauf eine frische Funktion. t in eine Abhaengigkeitsliste einzutragen ist damit in diesem Projekt nachweislich eine Schleife.

Der Griff lautet deshalb ueberall gleich und kommt ohne Ausnahme-Kommentar und ohne Referenz-Tricks aus: den uebersetzten Text vor dem Hook in eine gewoehnliche Konstante legen und diese Konstante in die Abhaengigkeitsliste schreiben. React vergleicht Zeichenketten per Wert, nicht per Identitaet — die Liste ist damit von Durchlauf zu Durchlauf gleich, der Effekt laeuft weiterhin genau einmal, und die Abhaengigkeit ist ehrlich benannt statt versteckt.

Regeln fuer diesen Vorgang

  • t kommt in keine Abhaengigkeitsliste. Nirgends.
  • Ein biome-ignore gibt es nur dreimal (Befunde 15, 16, 17), jeweils mit deutschem Grund in einer Zeile.
  • Die vorhandenen eslint-disable-Zeilen fuer diese Regel sind wirkungslos: in diesem Projekt gibt es keine ESLint-Konfiguration mehr, Biome hat sie abgeloest. Alle 11 liegen in Dateien, die hier angefasst werden, und verschwinden dabei — entweder ersatzlos oder durch die biome-ignore-Zeile ersetzt.
  • Kein Umbau ueber den Befund hinaus, kein neues Paket, keine Versionsanhebung, keine Formatierung fremder Dateien (D-06).

<execution_context> @/.claude/gsd-core/workflows/execute-plan.md @/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/STATE.md @CLAUDE.md

@apps/web/src/components/dashboard/widgets/calendar-month.ts @apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx @apps/web/src/app/(portal)/modules/tender-radar/page.tsx

Aufgabe 1: Kalender und Stoppuhr — die Effekte mit Taktgeber (Befunde 1-6) apps/web/src/components/dashboard/widgets/calendar-widget.tsx, apps/web/src/components/dashboard/widgets/calendar-widget.test.tsx, apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx, apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx - apps/web/src/components/dashboard/widgets/calendar-widget.tsx Zeilen 60-150 (Zustand, Merkung, Ladeeffekt, showPrev/showNext/showToday) - apps/web/src/components/dashboard/widgets/calendar-month.ts Zeilen 40-70 (`resolveCalendarConfig`) und `computeFetchWindow` - apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx Zeilen 45-105 (`computeElapsed`, Zustand, Takt-Effekt) - beide zugehoerigen Testdateien, jeweils Kopf und die vorhandenen Faelle - Kalender, Ersteinblendung: genau ein `fetchSources` und genau ein `fetchEvents`. - Kalender, ein Monat weiter: genau ein zusaetzlicher `fetchEvents`. - Kalender, Druck auf den Monatsknopf waehrend der laufende Monat bereits angezeigt wird: KEIN zusaetzlicher `fetchEvents`. Das ist die Probe, die den naiven Griff auffliegen laesst. - Kalender, gleiche Konfiguration, erneutes Zeichnen der Kachel: KEIN zusaetzlicher Abruf. - Stoppuhr, laufend, eine Runde aufgezeichnet: die Anzeige laeuft weiter, springt nicht zurueck auf null und ueberspringt keine Sekunde. - Stoppuhr, Start-Runde-Stopp-Reset: genau vier Schreibvorgaenge an `updateWidgetConfig`, einer je Klick. Befunde 1 und 2, `calendar-widget.tsx` Zeile 76: Die Merkung um `resolveCalendarConfig(config)` ersatzlos aufloesen und die drei Werte direkt beim Zeichnen bestimmen — also `showMonth`, `maxEvents` und `lookaheadDays` unmittelbar aus `resolveCalendarConfig(config)` herausziehen, ohne `useMemo`. Das ist zulaessig, weil `resolveCalendarConfig` eine reine Funktion ohne Seiteneffekte ist und ausschliesslich drei einfache Werte liefert: die Merkung hat nie etwas gespart, und weil nur einfache Werte weitergereicht werden, bleibt jeder nachgelagerte Vergleich unveraendert. Die zugehoerige wirkungslose `eslint-disable`-Zeile entfaellt mit. Kein `biome-ignore` an dieser Stelle.

Befunde 3 und 4, calendar-widget.tsx Zeile 82: Zuerst showToday identitaetserhaltend machen — den Monatszustand ueber die Aktualisierungsform setzen und das bisherige Datum unveraendert zurueckgeben, wenn es bereits auf dem gewuenschten Monatsersten steht; nur bei echtem Monatswechsel ein neues Datum einsetzen. Erst danach die Abhaengigkeitsliste des Ladeeffekts von dem Aufruf monthDate.getTime() auf monthDate selbst umstellen. Die Reihenfolge ist Pflicht: ohne den ersten Schritt loest der zweite bei jedem Druck auf den Monatsknopf einen Termin-Abruf aus, der ueber die API bis zum Exchange-Server durchschlaegt (D-04). Der Kommentar oberhalb des Effekts wird auf den neuen Stand gebracht: das Ladefenster bleibt auf lokale Tagesgrenzen gerundet, damit der Zwischenspeicher-Schluessel des Backends ueber die Fuenf-Minuten-Auffrischung stabil bleibt — daran aendert sich nichts. Die wirkungslose eslint-disable-Zeile entfaellt.

Befunde 5 und 6, stopwatch-widget.tsx Zeile 81: Auf Modulebene eine kleine reine Hilfsfunktion ergaenzen, die aus Startzeitpunkt und bereits gesammelter Dauer die aktuelle Dauer rechnet; computeElapsed ruft sie im laufenden Fall auf, sodass es bei identischem Ergebnis bleibt. Im Bauteil die drei benoetigten Werte vor dem Effekt aus dem Zustandsobjekt herausziehen und im Effekt ausschliesslich diese drei Werte und die neue Hilfsfunktion verwenden — das ganze Zustandsobjekt wird im Effekt nicht mehr angefasst. Die Abhaengigkeitsliste bleibt inhaltlich dieselbe und benennt jetzt genau das, was der Effekt liest. Ergebnis: der Takt wird weiterhin nur beim Wechsel zwischen laufend und angehalten sowie bei einer Zeitkorrektur neu aufgesetzt, nicht beim Aufzeichnen einer Runde. Die wirkungslose eslint-disable-Zeile entfaellt. Kein biome-ignore an dieser Stelle.

Danach die beiden Testdateien erweitern. In calendar-widget.test.tsx zwei Faelle ergaenzen, die den Aufrufzaehler der Attrappe mockFetchEvents pruefen: zweimal weiterblaettern ergibt drei Abrufe, dreimal auf den Monatsknopf druecken waehrend der laufende Monat angezeigt wird ergibt weiterhin einen. Beide Proben sind Rueckfallsicherungen, keine RED-zuerst-Proben: sie sind auch vor dem Umbau gruen und schlagen genau dann fehl, wenn jemand den naiven Griff waehlt — das im Testkommentar so hinschreiben. In stopwatch-widget.test.tsx einen Fall mit gestellter Uhr ergaenzen: starten, Zeit vorruecken, eine Runde aufzeichnen, weiter vorruecken, und pruefen, dass die angezeigte Dauer danach groesser ist als vor der Runde und nicht auf null zurueckgefallen ist; zusaetzlich den Aufrufzaehler der Schreib-Attrappe gegen die Zahl der Klicks pruefen. cd /home/vicolab/projects/tessera-ctl && npx biome lint apps/web/src/components/dashboard/widgets/calendar-widget.tsx apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx --reporter=json --max-diagnostics=2000 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);const ds=j.diagnostics||[];const e=ds.filter(x=>(x.category||"").includes("useExhaustiveDependencies"));console.log("total="+ds.length+" exhaustive="+e.length);process.exit(e.length===0?0:1);})' cd /home/vicolab/projects/tessera-ctl && pnpm -C apps/web exec vitest run src/components/dashboard/widgets/calendar-widget.test.tsx src/components/dashboard/widgets/stopwatch-widget.test.tsx cd /home/vicolab/projects/tessera-ctl && test "$(grep -c 'eslint-disable-next-line react-hooks/exhaustive-deps' apps/web/src/components/dashboard/widgets/calendar-widget.tsx apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx | awk -F: '{s+=$2} END {print s}')" = "0" Am laufenden System, Kalender (D-04): Stapel mit docker compose up -d --build db api web starten, im Browser auf http://localhost:3000 als admin/admin123 anmelden, das Dashboard mit der Kalender-Kachel oeffnen. Ueber das Netzwerkprotokoll des Browsers (Playwright-MCP browser_network_requests, NICHT per fetch aus der Seite heraus) 60 Sekunden lang zaehlen: genau ein /calendar/sources und genau ein /calendar/events. Dann dreimal auf den Monatsknopf in der Mitte druecken — die Zahl der /calendar/events darf sich NICHT erhoehen. Dann zweimal weiterblaettern — genau zwei zusaetzliche. Steigt die Zahl beim Monatsknopf, ist der naive Griff drin und die Aufgabe ist nicht erledigt. Am laufenden System, Stoppuhr (D-05): Stoppuhr-Kachel starten, 20 Sekunden zusehen, eine Runde aufzeichnen, weitere 10 Sekunden zusehen, stoppen, zuruecksetzen. Zu beurteilen ist, was auf dem Bildschirm passiert — laeuft die Zeit gleichmaessig weiter, springt sie beim Aufzeichnen der Runde, laeuft sie doppelt so schnell, faellt sie zurueck? Das kann nur ein Mensch beurteilen. Parallel im Netzwerkprotokoll: genau vier PATCH /dashboard/widgets/.../config, einer je Klick, kein Dauerfeuer. Die Befunde 1 bis 6 sind weg, beide Widget-Testdateien laufen gruen und enthalten die neuen Zaehlproben, der Monatsknopf loest im laufenden Monat keinen Abruf aus, und die Stoppuhr laeuft ueber Runde und Stopp hinweg sichtbar sauber weiter.

Aufgabe 2: Die t-Falle und die instabilen Ladefunktionen (Befunde 7-15) apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx, apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.tsx, apps/web/src/app/(portal)/modules/tender-radar/components/SavedSearchBar.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.tsx, apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.test.tsx, apps/web/src/components/dashboard/widgets/favorites-widget.tsx, apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx - je Datei nur der Hook-Bereich laut Befundtabelle, plus rund 15 Zeilen davor und danach - apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.test.tsx Zeilen 1-65 (Attrappen-Muster fuer next-intl und den API-Client) - apps/web/src/app/(portal)/modules/tender-radar/page.tsx Zeilen 40-105 (wer `refreshKey` hochzaehlt) - Jedes der acht Bauteile fuehrt beim Einhaengen genau einen Abruf aus. - Ein erneutes Zeichnen desselben Bauteils mit unveraenderten Eigenschaften fuehrt zu KEINEM weiteren Abruf. Das ist die entscheidende Probe: die Uebersetzungs-Attrappe in den Tests liefert bei jedem Durchlauf eine frische Funktion, eine Rueckkehr von `t` in die Abhaengigkeitsliste faellt hier sofort auf. - Trefferliste: ein Bump von `refreshKey` fuehrt zu genau einem weiteren Abruf. - Die angezeigten Fehlermeldungen bleiben Wort fuer Wort dieselben wie bisher. Fuer die Befunde 7 bis 14 ueberall derselbe Griff, Datei fuer Datei einzeln durchgehen und jeweils die Ersatz-Fehlertexte aus dem Effekt beziehungsweise dem Rueckruf herausziehen: den Aufruf von `t` mit dem festen Schluessel direkt im Bauteil-Rumpf ausfuehren, das Ergebnis einer sprechend benannten Konstanten zuweisen (etwa `loadErrorText`, `saveErrorText`) und im Effekt beziehungsweise Rueckruf nur noch diese Konstante verwenden. Anschliessend die Konstante in die Abhaengigkeitsliste aufnehmen. Weil es sich um eine Zeichenkette handelt, vergleicht React per Wert: die Liste ist bei jedem Durchlauf gleich, der Effekt laeuft weiterhin genau einmal beim Einhaengen. Die wirkungslosen `eslint-disable`-Zeilen entfallen dabei.

Betroffen sind im Einzelnen: VehicleTable.tsx Zeile 182 (Ladefunktion, Ersatztext des Ladefehlers — der vorhandene Kommentar ueber die ausgelassene Abhaengigkeit wird durch eine kurze Notiz ersetzt, die den neuen Griff erklaert), TenderDetail.tsx Zeile 96, DigestIntervalForm.tsx Zeile 32, SourceConfigForm.tsx Zeile 44 und favorites-widget.tsx Zeile 99.

RssFeedListForm.tsx Zeile 74 und SavedSearchBar.tsx Zeile 141 brauchen einen Schritt mehr: dort ist die Ladefunktion eine gewoehnliche Funktion im Rumpf, die bei jedem Durchlauf neu entsteht. Nach dem Herausziehen des Ersatz-Fehlertextes die Ladefunktion in einen stabilen Rueckruf einpacken, dessen Abhaengigkeitsliste nur noch die Text-Konstante enthaelt, und im Effekt die Ladefunktion als einzige Abhaengigkeit fuehren. Die Ladefunktion wird an beiden Stellen auch aus Bedienschritten heraus aufgerufen — das bleibt unveraendert moeglich.

ResultsList.tsx Zeilen 89 bis 130: zuerst derselbe Griff fuer den Ersatz-Fehlertext (Befund 8). Fuer Befund 15 zusaetzlich refreshKey aus der Abhaengigkeitsliste der Ladefunktion herausnehmen und stattdessen in die Liste des Effekts schreiben, der die Ladefunktion aufruft. Das ist dieselbe Bauform, die InvoiceHistoryTable bereits verwendet, und verhaelt sich identisch: ein Bump loest weiterhin genau einen Abruf aus. Der Vorteil: die Ausnahme beschraenkt sich damit auf den winzigen Effekt, waehrend die grosse Ladefunktion vollstaendig unter der Regel bleibt. Ueber diesen Effekt eine biome-ignore-Zeile fuer lint/correctness/useExhaustiveDependencies setzen, mit einem deutschen Grund in einer Zeile, der sagt, dass der Zaehler der Elternseite ein Auffrisch-Ausloeser ist und die Trefferliste ohne ihn nach Jetzt abrufen auf dem alten Stand bliebe.

Danach die Tests. In den sieben vorhandenen Testdateien (VehicleTable, ResultsList, TenderDetail, SavedSearchBar, RssFeedListForm, SourceConfigForm, favorites-widget) je einen Fall ergaenzen, der zaehlt statt zu zeichnen: Bauteil einhaengen, das Ende des ersten Abrufs abwarten, dasselbe Bauteil mit unveraenderten Eigenschaften erneut zeichnen lassen, und pruefen, dass die Attrappe des API-Clients weiterhin genau einmal aufgerufen wurde. Im Testkommentar festhalten, wogegen die Probe sichert: die Uebersetzungs-Attrappe liefert bei jedem Durchlauf eine frische Funktion, also faellt eine Rueckkehr von t in die Abhaengigkeitsliste hier sofort auf. In ResultsList.test.tsx zusaetzlich einen Fall, der nach einem Bump von refreshKey genau einen weiteren Abruf erwartet — damit ist die stehengelassene Abhaengigkeit als tragend belegt und nicht nur behauptet.

Fuer DigestIntervalForm gibt es heute keine Testdatei. Hier wird bewusst keine angelegt: das Bauteil wird stattdessen am laufenden System auf der Seite Meine Quellen nachgezaehlt. Diese Luecke gehoert so in das SUMMARY. cd /home/vicolab/projects/tessera-ctl && npx biome lint "apps/web/src/app/(portal)/modules" apps/web/src/components/dashboard/widgets/favorites-widget.tsx --reporter=json --max-diagnostics=5000 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);const e=(j.diagnostics||[]).filter(x=>(x.category||"").includes("useExhaustiveDependencies"));console.log("exhaustive="+e.length);process.exit(e.length===0?0:1);})' cd /home/vicolab/projects/tessera-ctl && pnpm -C apps/web exec vitest run "src/app/(portal)/modules" src/components/dashboard/widgets/favorites-widget.test.tsx cd /home/vicolab/projects/tessera-ctl && test "$(grep -c 'biome-ignore lint/correctness/useExhaustiveDependencies' "apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx")" = "1" cd /home/vicolab/projects/tessera-ctl && pnpm -C apps/web type-check Am laufenden System: Ausschreibungsradar oeffnen, 60 Sekunden im Netzwerkprotokoll des Browsers zaehlen — genau ein /tenders und ein /triage, kein Nachschlag. Dann Jetzt abrufen druecken: genau ein weiterer /tenders. Dann Meine Quellen oeffnen und 60 Sekunden zaehlen — je genau ein Abruf fuer Zustell-Einstellung, RSS-Feeds und Quellen-Konfiguration. Dann in DKV Flotte die Fahrzeugtabelle oeffnen, ein Fahrzeug speichern und pruefen, dass die Tabelle den neuen Stand zeigt (Gegenprobe gegen eingefrorene Anzeige) und dabei kein Dauerfeuer entsteht. Die Befunde 7 bis 15 sind weg, ResultsList.tsx traegt genau eine begruendete Ausnahme, alle sieben erweiterten Testdateien laufen gruen und enthalten je eine Zaehlprobe, die Typpruefung von apps/web ist sauber, und am laufenden System entsteht auf keiner der drei Seiten ein wiederholter Abruf.

Aufgabe 3: Absicht, Ballast und die zwei fehlenden stabilen Abhaengigkeiten (Befunde 16-21) apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx, apps/web/src/components/layout/sidebar.tsx, apps/web/src/components/layout/sidebar.test.tsx, apps/web/src/app/(portal)/admin/modules/components/ActivateModuleDialog.tsx, apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.tsx, apps/web/src/app/(portal)/admin/groups/components/GroupMembersModal.test.tsx, apps/web/src/app/(portal)/admin/modules/grants/page.tsx - apps/web/src/app/(portal)/admin/modules/grants/grants-matrix.test.tsx (Attrappen-Muster fuer globales fetch auf den Verwaltungsseiten — Vorlage fuer die neue Testdatei) - apps/web/src/components/layout/sidebar.test.tsx Kopf und vorhandene Faelle - apps/web/src/app/(portal)/admin/modules/page.tsx Zeilen 225-250 (wie der Aktivierungs-Dialog eingehaengt wird) - apps/web/src/app/(portal)/admin/groups/page.tsx Zeilen 260-275 (wie der Mitglieder-Dialog eingehaengt wird) - Seitenleiste: Einhaengen ergibt genau einen Abruf der aktiven Module; ein Bump des Auffrisch-Zaehlers ergibt genau einen weiteren; ein erneutes Zeichnen ohne Bump ergibt keinen weiteren. - Mitglieder-Dialog: Einhaengen ergibt genau einen Abruf der Mitglieder und genau einen der Benutzer; erneutes Zeichnen ergibt keine weiteren; ein Wechsel der Gruppe ergibt genau einen weiteren Mitglieder-Abruf mit der neuen Kennung. - Freigabe-Matrix: die bestehenden Suchfaelle verhalten sich unveraendert — ein Begriff, der nur eine Achse trifft, leert die andere Achse nicht. Befund 16, `InvoiceHistoryTable.tsx` Zeile 81: `refreshKey` bleibt in der Liste des Effekts stehen. Darueber eine `biome-ignore`-Zeile fuer `lint/correctness/useExhaustiveDependencies` setzen mit einem deutschen Grund in einer Zeile: der Zaehler der Elternseite ist ein Auffrisch-Ausloeser, ohne ihn bliebe die Historie nach Jetzt pruefen auf dem alten Stand.

Befund 17, sidebar.tsx Zeile 57: genauso — der Auffrisch-Zaehler aus dem Marketplace-Speicher bleibt stehen, mit deutscher Begruendung darueber: ohne ihn erscheint ein frisch aktiviertes Modul in der Seitenleiste erst nach einem Neuladen der Seite, die Navigation liefe also dem Berechtigungsstand hinterher.

Befund 18, ActivateModuleDialog.tsx Zeile 47: moduleId aus der Abhaengigkeitsliste entfernen, open bleibt. Belegt ist, dass das folgenlos ist: die Elternseite haengt den Dialog innerhalb einer Bedingung je gewaehltem Modul frisch ein und gibt open dort fest als wahr mit — moduleId kann sich waehrend der Lebenszeit des Dialogs nicht aendern, und der Effekt holt ohnehin nur die Gruppenliste, die vom Modul unabhaengig ist. Kein biome-ignore an dieser Stelle.

Befunde 19 und 20, GroupMembersModal.tsx Zeile 89: beide Ladefunktionen in die Abhaengigkeitsliste des Effekts aufnehmen und die leere Liste ersetzen. Das ist gefahrlos, weil beide bereits in stabile Rueckrufe eingepackt sind: die eine haengt nur an der Gruppenkennung, die andere an nichts. Die wirkungslose eslint-disable-Zeile und der Kommentar ueber die vermeintlich stabile Gruppenkennung entfallen — die Stabilitaet steht jetzt in der Liste statt in einem Kommentar.

Befund 21, grants/page.tsx Zeile 140: die Hilfsfunktion fuer den Suchabgleich aus dem Bauteil-Rumpf in den Rumpf der Merkung verschieben, sodass die Merkung nur noch die Modulliste, die Gruppenliste und den kleingeschriebenen Suchbegriff liest — allesamt bereits in der Liste. Die Wahrheitstabelle aus dem vorhandenen Kommentar bleibt Wort fuer Wort gueltig und unveraendert; verschoben wird nur, wo die Funktion steht. Die wirkungslose eslint-disable-Zeile entfaellt. Kein biome-ignore an dieser Stelle.

Danach die Tests. In sidebar.test.tsx einen Fall ergaenzen, der belegt, dass die stehengelassene Abhaengigkeit tragend ist: Seitenleiste einhaengen, Abruf abwarten, den Auffrisch-Zaehler im Speicher hochsetzen, und pruefen, dass genau ein weiterer Abruf der aktiven Module erfolgt ist — und dass ein erneutes Zeichnen ohne Bump keinen weiteren ausloest. Zu GroupMembersModal eine neue Testdatei anlegen, nach dem Attrappen-Muster der Freigabe-Matrix (globales fetch als Attrappe, next-intl als Attrappe): einhaengen ergibt genau einen Mitglieder- und einen Benutzer-Abruf, erneutes Zeichnen keine weiteren, und ein Wechsel der uebergebenen Gruppe genau einen weiteren Mitglieder-Abruf mit der neuen Kennung — der letzte Fall ist der eigentliche Beleg fuer die Einstufung als Defekt und faellt ohne den Fix durch.

Zum Abschluss den Gesamtstand messen und das SUMMARY schreiben: die Tabelle aller 21 Befunde mit Datei, Zeile, Kategorie und der Begruendung in einer Zeile, dazu die drei begruendeten Ausnahmen mit Fundstelle, die Zaehlung vorher und nachher, die Ergebnisse der Proben am laufenden System und die offen benannte Luecke bei DigestIntervalForm (keine Testdatei, nur am laufenden System geprueft). Wortlaut wie Dependency hinzugefuegt genuegt als Begruendung nicht (D-01). cd /home/vicolab/projects/tessera-ctl && npx biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);const ds=j.diagnostics||[];const e=ds.filter(x=>(x.category||"").includes("useExhaustiveDependencies"));const err=ds.filter(x=>x.severity==="error");console.log("total="+ds.length+" exhaustive="+e.length+" errors="+err.length);process.exit(ds.length===446&&e.length===0&&err.length===0?0:1);})' cd /home/vicolab/projects/tessera-ctl && pnpm lint 2>&1 | tail -3 cd /home/vicolab/projects/tessera-ctl && test "$(grep -rl 'eslint-disable-next-line react-hooks/exhaustive-deps' apps/web/src | wc -l)" = "0" cd /home/vicolab/projects/tessera-ctl && test "$(grep -rl 'biome-ignore lint/correctness/useExhaustiveDependencies' apps/web/src | wc -l)" = "3" cd /home/vicolab/projects/tessera-ctl && pnpm -C apps/web exec vitest run 2>&1 | tail -6 cd /home/vicolab/projects/tessera-ctl && pnpm -C apps/api exec vitest run 2>&1 | tail -6 cd /home/vicolab/projects/tessera-ctl && pnpm type-check 2>&1 | tail -3 Am laufenden System: als admin ein Modul im Marketplace aktivieren und pruefen, dass es ohne Neuladen in der Seitenleiste erscheint (Beleg fuer Befund 17). In der Modulverwaltung den Aktivierungs-Dialog oeffnen und im Netzwerkprotokoll pruefen, dass genau ein /groups geholt wird (Befund 18). In der Gruppenverwaltung den Mitglieder-Dialog einer Gruppe oeffnen, schliessen, den einer zweiten Gruppe oeffnen und pruefen, dass die angezeigten Mitglieder zur zweiten Gruppe gehoeren und nicht zur ersten (Befunde 19/20). In DKV Flotte Jetzt pruefen druecken und pruefen, dass die Historie genau einen zusaetzlichen Abruf macht (Befund 16). Alle 21 Befunde sind entschieden und umgesetzt, der Gesamtstand liegt bei 446 Meldungen mit null Befunden der Regel und null Fehlern, pnpm lint bleibt 5/5, beide Testlaeufe sind gruen und ihre Zahlen liegen nicht unter dem Ausgangsstand, in apps/web/src steht keine wirkungslose eslint-Ausnahme mehr und genau drei begruendete biome-ignore-Zeilen, und das SUMMARY traegt die vollstaendige Tabelle mit Begruendungen.

<threat_model>

Vertrauensgrenzen

Grenze Beschreibung
Browser -> eigene API (:3001) Die Zahl der Anfragen wird allein vom Browser-Code bestimmt. Eine Effekt-Schleife ist eine selbst verursachte Last, gegen die die API keine Drosselung besitzt.
API -> Exchange/EWS Das Kalender-Widget fragt ueber die API einen fremden Mail-Server ab. Ein Zwischenspeicher mit fuenf Minuten Haltezeit liegt dazwischen, dessen Schluessel aus dem auf Tagesgrenzen gerundeten Ladefenster entsteht.
API -> Ausschreibungsportale und RSS-Quellen Das Ausschreibungsradar holt ueber die API Daten aus fremden Quellen.
Browser -> Berechtigungsanzeige Seitenleiste, Freigabe-Matrix und Mitglieder-Dialog zeigen Berechtigungszustand. Ein Effekt, der nicht neu laeuft, zeigt einen alten Stand.

STRIDE-Register

Threat ID Kategorie Komponente Schwere Umgang Massnahme
T-GOF-01 Denial of Service Alle 16 Hook-Stellen in apps/web high mitigate t und im Rumpf neu entstehende Funktionen kommen in keine Abhaengigkeitsliste; stattdessen wird der uebersetzte Text als Zeichenkette vorgezogen (Wertvergleich) beziehungsweise die Funktion stabil eingepackt. Nachweis nicht per Behauptung, sondern per Zaehlung: rund zehn Zaehlproben in den Tests plus Zaehlung im Netzwerkprotokoll des Browsers ueber 60 Sekunden je Seite.
T-GOF-02 Denial of Service calendar-widget.tsx, Ladeeffekt Zeile 82 high mitigate Der naive Griff (monthDate direkt eintragen) wuerde bei jedem Druck auf den Monatsknopf einen Termin-Abruf ausloesen, der ueber die API bis zum Exchange-Server durchschlaegt und den Dienst-Zugang drosseln oder sperren koennte. Deshalb zuerst showToday identitaetserhaltend machen, erst danach die Liste umstellen; Nachweis durch dreimaliges Druecken des Monatsknopfs ohne Anstieg der Abrufzahl. Das auf Tagesgrenzen gerundete Ladefenster bleibt unangetastet, damit der Zwischenspeicher des Backends weiter greift.
T-GOF-03 Denial of Service ResultsList.tsx, RssFeedListForm.tsx high mitigate Beide Bauteile haengen an fremden Quellen (Ausschreibungsportale, RSS-Feeds). Die Ladefunktionen werden stabil eingepackt statt Abhaengigkeiten blind zu ergaenzen; die Zaehlprobe nach erneutem Zeichnen ist Teil der Abnahme.
T-GOF-04 Tampering sidebar.tsx Zeile 57, grants/page.tsx Zeile 140 medium mitigate Der Auffrisch-Ausloeser der Seitenleiste bleibt erhalten und wird begruendet, damit die Navigation nach einer Modul-Aktivierung nicht hinter dem Berechtigungsstand zurueckbleibt; ein Testfall belegt, dass der Ausloeser wirkt. Bei der Freigabe-Matrix wird nur verschoben, wo die Hilfsfunktion steht — die bestehenden Suchfaelle bleiben als Beleg gruen.
T-GOF-05 Information Disclosure GroupMembersModal.tsx Zeile 89 medium mitigate Ohne die beiden stabilen Abhaengigkeiten zeigt der Dialog bei einem Gruppenwechsel ohne Neuaufbau die Mitglieder der vorigen Gruppe. Die Abhaengigkeiten werden ergaenzt und der Gruppenwechsel wird mit einem eigenen Testfall belegt.
T-GOF-06 Tampering Paketinstallationen low accept Dieser Vorgang installiert nichts und hebt keine Version an (D-06). Es gibt keine Installationsaufgabe, damit keine Angriffsflaeche ueber die Paketbeschaffung.
</threat_model>
## Gesamtnachweis

Maschinell, am Schluss von Aufgabe 3:

  1. Biome gesamt: total=446 exhaustive=0 errors=0 (heute total=467 exhaustive=21 errors=0). Die Differenz ist genau 21 — je Befund eine Meldung weniger, kein Anstieg an anderer Stelle (D-07). Liegt die Zahl ueber 446, hat der Umbau oder eine der neuen Testdateien eine neue Meldung erzeugt: die ist zu bereinigen, nicht wegzuerklaeren. Liegt sie darunter, ist versehentlich an anderer Stelle mitaufgeraeumt worden — auch das gehoert benannt, weil es den Vergleich verfaelscht.
  2. pnpm lint bleibt 5/5.
  3. pnpm type-check bleibt 4/4.
  4. pnpm -C apps/web exec vitest run: gruen, Dateien und Faelle nicht unter 66 / 462 (Ausgangsstand 21.09.).
  5. pnpm -C apps/api exec vitest run: gruen, nicht unter 71 / 1136 — diese Aufgabe fasst apps/api nicht an, der Lauf ist die Gegenprobe.
  6. In apps/web/src keine Datei mehr mit einer wirkungslosen eslint-Ausnahme fuer diese Regel, genau drei Dateien mit einer begruendeten biome-ignore-Zeile.

Am laufenden System (Stapel: docker compose up -d --build db api web, Web auf 3000, API auf 3001, Anmeldung admin/admin123 — die Datenbank bleibt, wie sie ist):

Gemessen wird ausschliesslich ueber das Netzwerkprotokoll des Browsers (Playwright-MCP browser_network_requests) oder ersatzweise ueber den Netzwerk-Reiter der Entwicklerwerkzeuge von Hand. Niemals per fetch aus der Seite heraus — das misst etwas anderes und taeuscht in beide Richtungen.

Ansicht Zaehlung Erwartung
Dashboard, Kalender-Kachel 60 s ruhen lassen 1x /calendar/sources, 1x /calendar/events
Dashboard, Kalender-Kachel 3x Monatsknopf im laufenden Monat kein zusaetzlicher /calendar/events
Dashboard, Kalender-Kachel 2x weiterblaettern genau 2 zusaetzliche /calendar/events
Dashboard, Stoppuhr Start, Runde, Stopp, Reset genau 4x PATCH .../config; Anzeige laeuft sichtbar sauber (Menschenurteil)
Ausschreibungsradar 60 s ruhen lassen 1x /tenders, 1x /triage
Ausschreibungsradar Jetzt abrufen genau 1 zusaetzlicher /tenders
Meine Quellen 60 s ruhen lassen je 1 Abruf fuer Zustell-Einstellung, RSS-Feeds, Quellen-Konfiguration
DKV Flotte Jetzt pruefen genau 1 zusaetzlicher Historien-Abruf
DKV Flotte, Fahrzeuge Fahrzeug speichern Tabelle zeigt den neuen Stand, kein Dauerfeuer
Marketplace Modul aktivieren Modul erscheint ohne Neuladen in der Seitenleiste
Modulverwaltung Aktivierungs-Dialog oeffnen genau 1x /groups
Gruppenverwaltung Dialog Gruppe A schliessen, Gruppe B oeffnen angezeigte Mitglieder gehoeren zu Gruppe B

Ausdruecklich menschliches Urteil (nicht automatisierbar, nicht als maschinelle Probe verkleidet): das Laufverhalten der Stoppuhr auf dem Bildschirm ueber Start, Runde und Stopp hinweg — springt die Zeit, laeuft sie doppelt so schnell, faellt sie zurueck? Und die Frage, ob die Kalenderkachel nach dem Blaettern die Termine des richtigen Monats zeigt.

Abgrenzung

Phasen-Pruefpunkte (API-Abdeckungsmatrix, Annahme-Abgleich, Schema-Tor) entfallen: Schnellvorgang ohne Roadmap-Phase, ohne neue Fremd-API, ohne Schema-Aenderung. Es wird keine COVERAGE.md erzeugt.

<success_criteria>

  • Alle 21 Befunde sind einzeln entschieden, die Entscheidung steht mit Begruendung im SUMMARY, und keine Begruendung lautet sinngemaess nur, dass eine Abhaengigkeit ergaenzt wurde (D-01).
  • Genau drei Befunde stehen als Absicht mit begruendetem biome-ignore im Quelltext — 14 Prozent, deutlich unter der Drittel-Grenze (D-02).
  • Verhalten unveraendert bei den Kategorien Absicht, Falle und Ballast; veraendert nur in der beabsichtigten Richtung bei den beiden Defekten; nachgewiesen durch Zaehlungen, nicht durch Augenschein allein (D-03).
  • Kalender und Stoppuhr sind am laufenden System geprueft, nicht nur gelesen (D-04, D-05).
  • Kein neues Paket, keine Versionsanhebung, kein Umbau ueber die Befunde hinaus (D-06).
  • Biome gesamt 446 statt 467, null Befunde der Regel, null Fehler, pnpm lint 5/5 (D-07).
  • Beide Testlaeufe gruen, Zahlen nicht unter dem Ausgangsstand. </success_criteria>
Create `.planning/quick/260921-gof-effekt-abhaengigkeiten-in-react-21-befun/260921-gof-SUMMARY.md` when done.

Pflichtbestandteile des SUMMARY:

  1. Tabelle aller 21 Befunde: Datei, Zeile, Kategorie (A/B/C/D), Entscheidung, Begruendung in einer Zeile.
  2. Die drei begruendeten Ausnahmen mit Fundstelle und Wortlaut des Grundes.
  3. Zaehlung vorher/nachher: Biome gesamt, Regel-Befunde, Fehler, Testdateien und Faelle je Anwendung.
  4. Ergebnisse jeder Zeile der Tabelle aus dem Abschnitt Gesamtnachweis, mit den tatsaechlich gezaehlten Zahlen.
  5. Offen benannte Luecken — mindestens: DigestIntervalForm hat keine Testdatei und ist nur am laufenden System geprueft.