Files
tessera-ctl/.planning/quick/260921-jt4-barrierefreiheit-mit-bedienentscheidunge/260921-jt4-SUMMARY.md
T
schalli 8d604b855a
Tessera CI/CD / Lint & Type Check (push) Successful in 53s
Tessera CI/CD / Tests (push) Successful in 1m14s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 19s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m38s
docs(quick-260921-jt4): Barrierefreiheit 30 -> 1, vier Restposten erledigt
Zusammenfassung und STATE.md zum Quick-Vorgang 260921-jt4.

Der Planer hat zwei Vorgaben des Orchestrators widerlegt: der
vorgesehene Rueckfallweg mit role und Tastaturhandler tauscht gemessen
drei Befunde gegen einen neuen, und vier der elf vermeintlichen
Klick-Befunde sind onError-Handler an Bildern, also gar keine
Bedienung. Alle fuenf ARIA-Befunde waren Beschriftungen auf rollenlosen
Elementen, die Vorleseprogramme still verwerfen.

Laufzeitnachweis vom Orchestrator: drei Monatswechsel holen die
Quellenliste nur noch einmal statt viermal, der Termin-Abruf mit
identischem Zeitraum ist weg. Der 5-Minuten-Auffrischer ist per
gestellter Uhr als intakt belegt, nicht per Warten im Browser - zwei
solche Messversuche waren ungueltig, weil das Werkzeug die Seite
zwischendurch neu laedt.

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

27 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed, status
phase plan subsystem tags requires provides affects actuals tech-stack key-files key-decisions patterns-established requirements-completed duration completed status
quick-260921-jt4 01 ui
a11y
biome
next-intl
react
calendar
i18n
phase provides
quick-260921-bi2 155 a11y-Fixes ueber sechs Regeln; die fuenf hier bearbeiteten Regeln (30 Befunde) bewusst zurueckgestellt (D-05)
phase provides
quick-260921-gof 21 useExhaustiveDependencies-Befunde beurteilt; die Faustregel 't gehoert in keine Abhaengigkeitsliste' als unbestaetigte Annahme hinterlassen
30 zurueckgestellte a11y-Befunde auf 1 gesenkt (429 -> 399 gesamt), Weg pro Fundstelle protokolliert
13 Oberflaechendateien mit echten Schaltflaechen statt klickbarer Bereiche statt Mausonly-Bedienung
Kalender-Widget ruft Quellenliste und Termine nicht mehr blind bei jedem Monatswechsel neu ab
t aus useTranslations ist am echten NextIntlClientProvider als identitaetsstabil ueber lokale Zustandswechsel nachgewiesen (widerlegt die gof-Annahme 'bei jedem Render frisch')
Uebersetzter, windows-sicherer ZIP-Dateiname im Zertifikat-Aufteiler (zip-filename.ts)
apps/web-a11y
dashboard-calendar-widget
i18n-catalogs
tokens tasks commits
21513 3 12
added patterns
Deckende Geschwister-Schaltflaeche statt role=button+tabIndex+onKeyDown: verschachtelte <button>-Elemente vermieden (MarketplaceCard-Muster, auch fuer Dialoghintergruende), ohne useSemanticElements neu auszuloesen.
role=toolbar statt role=group/region fuer beschriftete Schaltflaechenreihen ohne semantisches HTML-Aequivalent — role=group/region loesen useSemanticElements neu aus, role=toolbar nicht (an Biome 2.5.0 mit Projektkonfiguration gemessen).
Ref-basierte Sperrgriffe gegen verschwendete Effekte: hasSourcesRef/lastFetchWindowRef ausserhalb des Render-Zustands, mit einem expliziten force-Parameter fuer den Fall, der den Sperrgriff bewusst umgehen muss (5-Minuten-Auffrischer).
t-Identitaet aus useTranslations gegen den echten NextIntlClientProvider messen (nicht behaupten) — Testkomponente unter dem echten Provider, Zustandswechsel ausloesen, per toBe (Referenzgleichheit) pruefen.
Regex mit \x00-\x1F-Bereich vermeiden (lint/suspicious/noControlCharactersInRegex) — Steuerzeichen-Filterung zeichenweise statt per Regex, wenn das Filtern von Steuerzeichen der Zweck ist.
created modified
apps/web/src/app/(portal)/modules/cert-manager/zip-filename.ts
apps/web/src/app/(portal)/modules/cert-manager/zip-filename.test.ts
apps/web/src/components/dashboard/widget-catalog-modal.test.tsx
apps/web/src/components/settings/calendar-settings-panel.test.tsx
apps/web/src/lib/translations-identity.test.tsx
apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx
apps/web/src/components/dashboard/widget-catalog-modal.tsx
apps/web/src/components/settings/calendar-settings-panel.tsx
apps/web/src/components/dashboard/widgets/note-widget.tsx
apps/web/src/components/dashboard/widgets/note-task-list.tsx
apps/web/src/components/dashboard/widgets/calendar-widget.tsx
apps/web/src/components/dashboard/widgets/calculator-widget.tsx
apps/web/src/components/dashboard/widgets/favorites-widget.tsx
apps/web/src/components/layout/header.tsx
apps/web/src/components/settings/account-settings-form.tsx
apps/web/src/app/(auth)/login/page.tsx
apps/web/src/app/(auth)/reset-password/page.tsx
apps/web/src/app/(auth)/reset-password/[token]/page.tsx
apps/web/src/app/(portal)/change-password/page.tsx
apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx
apps/api/src/tenders/tender-normalizer.service.ts
apps/web/src/messages/de.json
apps/web/src/messages/en.json
D-01-Rueckfall (role=button+tabIndex+onKeyDown) an keiner der sechs Umbaustellen benutzt — der Planer hatte ihn empirisch geprueft und drei Befunde gegen einen neuen useSemanticElements-Befund getauscht; D-07 verbietet Wachstum anderswo. Deckende Geschwister-Schaltflaeche stattdessen bei MarketplaceCard, widget-catalog-modal und calendar-settings-panel.
calendar-widget Tageszelle wird NUR bei hasEvents zu einem <button> — 42 neue Tab-Stopps waeren eine Verschlechterung, nicht Tage ohne Termine.
Vier autoFocus-Stellen sind alle Seitenformulare, kein Dialog: Attribut entfernt (D-02), der ref+Effekt-Ersatzweg kam an keiner Stelle zum Zug.
t-Identitaet gemessen statt der gof-Annahme ein drittes Mal weitergetragen: bleibt bei einem echten NextIntlClientProvider-Test erhalten -> die vier verbliebenen t-Abhaengigkeitsstellen bleiben unveraendert.
ZIP-Dateiname darf sich nicht auf einen zufaellig harmlosen Uebersetzungstext verlassen — eigene, fuer sich getestete Schutzfunktion (zip-filename.ts) statt Vertrauen in den Katalogwert.
noUselessSwitchCase-Fallmarke in tender-normalizer.service.ts entfernt, Kommentar erweitert statt der Marke selbst eine Bedeutung zuzuschreiben, die die Regel nicht sehen kann — kein Verhaltenswechsel.
Vollstaendige a11y-Regelmenge (Ziel- UND zurueckgestellte Regeln) nach jedem Teilumbau neu messen, nicht nur die Zielregel — ein a11y-Fix kann eine ANDERE Regel neu ausloesen, wenn ein Element seine interaktive Klassifikation verliert.
D-01
D-02
D-03
D-04
D-05
D-06
D-07
~2h (eine Sitzung) 2026-09-21 complete

Quick-Vorgang 260921-jt4: 30 a11y-Befunde mit Bedienentscheidungen abgearbeitet Summary

30 zurueckgestellte Barrierefreiheits-Befunde auf 1 gesenkt (429 -> 399 gesamt, 0 Fehler) — 13 Oberflaechendateien mit echten Schaltflaechen statt klickbarer Bereiche, das Kalender-Widget ruft Quellenliste/Termine nicht mehr blind bei jedem Monatswechsel neu ab, und die gof-Annahme "t ist bei jedem Render frisch" ist am echten NextIntlClientProvider widerlegt statt ein drittes Mal weitergetragen.

Performance

  • Tasks: 3 von 3 (PLAN.md)
  • Commits: 12 (siehe Task Commits)
  • Dateien geaendert: 28 (23 Quell-/Testdateien + 2 i18n-Kataloge + PLAN.md/keine weiteren)
  • apps/web Tests: 66 -> 73 Dateien, 462 -> 529 Tests (Netto-Zuwachs 7 Dateien / 67 Tests)
  • apps/api Tests: unveraendert 72 Dateien / 1143 Tests (nur ein Kommentar/eine Fallmarke in tender-normalizer.service.ts geaendert, kein neuer Test noetig — die 18 vorhandenen Tests belegen weiterhin dasselbe Verhalten)

Accomplishments

  • 30 zurueckgestellte a11y-Befunde auf genau 1 gesenkt (der bewusst stehengelassene Taschenrechner-Tastaturhandler, D-07) — Biome-JSON 429 -> 399, errors 0 durchgehend
  • Fuenf Struktur-Umbauten (MarketplaceCard, widget-catalog-modal, calendar-settings-panel Loeschdialog, note-widget Kaestchen, calendar-widget Tageszelle) auf echte <button>-Elemente, jeder Umbau mit einem Test belegt, der eine ECHTE Tastaturbetaetigung ausloest (Enter/Leertaste auf einem fokussierten Element), nicht nur eine Klick-Attrappe
  • Kalender-Widget: Quellenliste und Termin-Ladefenster werden nicht mehr blind bei jedem Monatswechsel neu geholt — vier zaehlende Tests belegen Aufbau/abweichendes Fenster/identisches Fenster/erzwungener Auffrischer
  • translations-identity.test.tsx: t aus useTranslations ist am echten NextIntlClientProvider als identitaetsstabil ueber einen lokalen Zustandswechsel nachgewiesen — widerlegt die 260921-gof-Annahme "t ist bei jedem Render frisch"
  • Windows-sicherer, uebersetzter ZIP-Dateiname im Zertifikat-Aufteiler ueber eine neue, fuer sich getestete Schutzfunktion (zip-filename.ts), unabhaengig davon, ob der aktuelle Katalogwert zufaellig harmlos ist
  • Zwei Restposten aus 260921-bi2/gof geschlossen: ueberfluessige case-Marke in tender-normalizer.service.ts entfernt; keine neue Unterdrueckung (biome-ignore) im gesamten Diff

Task Commits

Aufgabe 1 (Aus klickbaren Bereichen echte Schaltflaechen machen, 5 Teilumbauten, jeder einzeln committet):

  1. MarketplaceCard.tsx — Karte - a8531d4 (fix)
  2. widget-catalog-modal.tsx — Hintergrund - 3d0bc0b (fix)
  3. calendar-settings-panel.tsx — Loeschdialog-Hintergrund - 0c89c13 (fix)
  4. note-widget.tsx + note-task-list.tsx — Aufgabenkaestchen - b601141 (fix)
  5. calendar-widget.tsx — Tageszelle - e651c24 (fix)

Aufgabe 2 (Rollen, Beschriftungen, Autofokus, zwei Restposten, jeder Teilumbau einzeln committet):

  1. autoFocus von vier Seitenformularen entfernt (D-02) - 9aa87bd (fix)
  2. calendar-settings-panel: drei Statussymbole role="img" (D-03) - b406a9c (fix)
  3. calculator-widget/favorites-widget: zwei Schaltflaechenreihen role="toolbar" (D-03) - f7b5df4 (fix)
  4. vier <img onError> mit aria-hidden (D-03) - 69fe706 (fix)
  5. tender-normalizer.service.ts: ueberfluessige case-Marke entfernt (Restposten 2) - 5a03b75 (refactor)
  6. ZIP-Name im Zertifikat-Aufteiler mit Windows-Schutzfunktion (Restposten 1) - 6c10c9b (feat)

Aufgabe 3 (Verschwendete Kalender-Abrufe + t-Identitaet, ein Commit):

  1. Kalender-Ladefenster/Quellenliste entdoppelt, t-Identitaet gemessen - f471b78 (fix)

Hinweis: keine separate Plan-Metadaten-Commit — diese SUMMARY und STATE.md/ROADMAP.md werden vom Orchestrator committet.

Files Created/Modified

  • apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx - Kartenklick als deckende Geschwister-Schaltflaeche
  • apps/web/src/components/dashboard/widget-catalog-modal.tsx - Hintergrund als benannte Schaltflaeche, totes stopPropagation entfernt
  • apps/web/src/components/settings/calendar-settings-panel.tsx - Loeschdialog-Hintergrund als Abbrechen-Schaltflaeche; drei Statussymbole role="img"
  • apps/web/src/components/dashboard/widgets/note-widget.tsx + note-task-list.tsx - Aufgabenkaestchen bedient sich selbst (onChange statt readOnly+delegiertem Klick)
  • apps/web/src/components/dashboard/widgets/calendar-widget.tsx - Tageszelle als Schaltflaeche bei Terminen; Quellenliste/Ladefenster-Sperrgriffe
  • apps/web/src/components/dashboard/widgets/calculator-widget.tsx - Speicherzeile role="toolbar", drei Texte in den Katalog gezogen
  • apps/web/src/components/dashboard/widgets/favorites-widget.tsx - Ansichtsumschalter role="toolbar" mit korrigierter Beschriftung; zwei <img> aria-hidden
  • apps/web/src/components/layout/header.tsx + apps/web/src/components/settings/account-settings-form.tsx - Avatar-<img onError> aria-hidden
  • apps/web/src/app/(auth)/login/page.tsx, reset-password/page.tsx, reset-password/[token]/page.tsx, apps/web/src/app/(portal)/change-password/page.tsx - autoFocus entfernt
  • apps/web/src/app/(portal)/modules/cert-manager/zip-filename.ts (neu) + .test.ts (neu) - Windows-sichere ZIP-Namens-Schutzfunktion
  • apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx - uebersetzter, sanitierter ZIP-Name
  • apps/api/src/tenders/tender-normalizer.service.ts - ueberfluessige case-Marke entfernt, Kommentar erweitert
  • apps/web/src/lib/translations-identity.test.tsx (neu) - t-Identitaet am echten Provider gemessen
  • apps/web/src/messages/de.json / en.json - 20 neue Schluessel, beide Kataloge weiterhin gleich lang (912/912)

Decisions Made

Siehe key-decisions im Frontmatter. Zusammengefasst: der D-01-Rueckfallweg wurde an keiner Stelle benutzt (empirisch als regelvermehrend erkannt); die Kalender-Tageszelle wird nur bei Terminen zu einem Tab-Stopp; alle vier autoFocus-Stellen sind Seiten, kein Dialog, deshalb Entfernen statt Ersatz; die t-Identitaetsfrage wurde gemessen statt behauptet und bestaetigt die vier verbliebenen Stellen als unveraendert richtig; der ZIP-Name haengt an einer eigenen Schutzfunktion, nicht am Zufall des Uebersetzungstexts.

Die 30 a11y-Fundstellen — gewaehlter Weg je Fundstelle

# Fundstelle Regel Gewaehlter Weg Begruendung (bei Rueckfall/Stehenlassen)
1 MarketplaceCard.tsx (Karte) noNoninteractiveElementInteractions echter Button (deckendes Geschwister) Karte enthaelt bereits eine Schaltflaeche im Fuss — unmittelbare <button>-Umwandlung waere eine Verschachtelung (Falle wie DropZone.tsx in 260921-bi2)
2 MarketplaceCard.tsx (Karte) noStaticElementInteractions echter Button (deckendes Geschwister) s. #1
3 MarketplaceCard.tsx (Karte) useKeyWithClickEvents echter Button (deckendes Geschwister) s. #1 — der Button traegt den Tastaturweg von selbst
4 widget-catalog-modal.tsx (Hintergrund) noNoninteractiveElementInteractions echter Button vorher rein optische Flaeche, jetzt fokussierbar und benannt
5 widget-catalog-modal.tsx (Hintergrund) noStaticElementInteractions echter Button s. #4
6 widget-catalog-modal.tsx (Hintergrund) useKeyWithClickEvents echter Button s. #4
7 widget-catalog-modal.tsx (Dialogflaeche) noNoninteractiveElementInteractions Handler entfaellt ersatzlos stopPropagation wird toter Code, sobald der Hintergrund Geschwister statt Vorfahr der Dialogflaeche ist
8 widget-catalog-modal.tsx (Dialogflaeche) useKeyWithClickEvents Handler entfaellt ersatzlos s. #7
9 calendar-settings-panel.tsx (Loeschdialog-Hintergrund) noNoninteractiveElementInteractions echter Button muss abbrechen, darf nie loeschen (T-JT4-04); Beschriftung nennt ausdruecklich das Abbrechen
10 calendar-settings-panel.tsx (Loeschdialog-Hintergrund) noStaticElementInteractions echter Button s. #9
11 calendar-settings-panel.tsx (Loeschdialog-Hintergrund) useKeyWithClickEvents echter Button s. #9
12 note-widget.tsx (Vorschau-Kaestchen) noNoninteractiveElementInteractions Kaestchen uebernimmt seinen Handler selbst readOnly entfernt, echtes onChange am Kaestchen statt delegiertem Klick am Behaelter
13 note-widget.tsx (Vorschau-Kaestchen) noStaticElementInteractions Kaestchen uebernimmt seinen Handler selbst s. #12
14 note-widget.tsx (Vorschau-Kaestchen) useKeyWithClickEvents Kaestchen uebernimmt seinen Handler selbst s. #12 — ein <input type="checkbox"> bedient sich per Definition mit der Tastatur
15 calendar-widget.tsx (Tageszelle) noNoninteractiveElementInteractions echter Button + Fokus-Handler, NUR bei Terminen 42 neue Tab-Stopps waeren eine Verschlechterung; nur Tage mit Terminen werden zu Tab-Stopps
16 calendar-widget.tsx (Tageszelle) noStaticElementInteractions echter Button + Fokus-Handler, NUR bei Terminen s. #15
17 login/page.tsx noAutofocus Attribut entfernt Seite, kein Dialog — D-02 verlangt Entfernen, nicht ref+Effekt
18 reset-password/[token]/page.tsx noAutofocus Attribut entfernt Seite, kein Dialog
19 reset-password/page.tsx noAutofocus Attribut entfernt Seite, kein Dialog
20 change-password/page.tsx noAutofocus Attribut entfernt Seite, kein Dialog — Nebennutzen: Fokus faellt jetzt nicht mehr am Hinweis zum erzwungenen Wechsel vorbei
21 calendar-settings-panel.tsx (Sync-Fehler-Symbol) useAriaPropsSupportedByRole role="img" + Katalogschluessel Beschriftung ist der einzige Text des Symbols (svg bereits aria-hidden); Rolle generic verwarf sie bisher stillschweigend
22 calendar-settings-panel.tsx (Verbindung-OK-Symbol) useAriaPropsSupportedByRole role="img" + vorhandener Schluessel connectionSuccess s. #21, Text passte woertlich, kein neuer Schluessel noetig
23 calendar-settings-panel.tsx (Verbindung-fehlgeschlagen-Symbol) useAriaPropsSupportedByRole role="img" + Katalogschluessel s. #21
24 calculator-widget.tsx (Speicherzeile) useAriaPropsSupportedByRole role="toolbar" + Katalogschluessel role="group"/"region" loesen useSemanticElements neu aus (gemessen); role="toolbar" nicht
25 favorites-widget.tsx (Ansichtsumschalter) useAriaPropsSupportedByRole role="toolbar" + KORRIGIERTE Beschriftung alte Beschriftung ("Favoriten") war sachlich falsch fuer einen Listen/Kachel-Umschalter — D-03 erlaubt hier das Streichen, eine richtige Beschriftung ist trotzdem besser als keine
26 header.tsx (Avatar <img onError>) noNoninteractiveElementInteractions aria-hidden="true" onError ist ein Ladefehler, keine Bedienung; alt="" bereits vorhanden — richtige Auszeichnung, aber kein Gewinn fuer einen Menschen
27 account-settings-form.tsx (Avatar <img onError>) noNoninteractiveElementInteractions aria-hidden="true" s. #26
28 favorites-widget.tsx (Symbol-Proxy <img onError>) noNoninteractiveElementInteractions aria-hidden="true" s. #26
29 favorites-widget.tsx (Symbol-Direkt <img onError>) noNoninteractiveElementInteractions aria-hidden="true" s. #26
30 calculator-widget.tsx (Rahmen, role="application" + onKeyDown) noNoninteractiveElementInteractions bleibt stehen, bleibt gezaehlt Der Handler FUEGT einen Tastaturweg hinzu (Ziffern erreichen die Logik, sobald irgendeine Taste fokussiert ist) — das Gegenteil des Schadens, den die Regel beschreibt. Alle drei Rollen-Alternativen gemessen, aendern nichts. addEventListener in einem Effekt ausdruecklich NICHT gewaehlt — das waere eine geschoente Zahl ohne Gegenwert (D-07).

Die vier Restposten

  1. ZIP-Name im Zertifikat-Aufteiler. downloadAllAsZip liegt ausserhalb der Komponente und konnte den Uebersetzungs-Hook nicht aufrufen — der Name kommt jetzt als Parameter herein (t('actions.zipFilename')). Der 260921-bi2-Einwand ("ein uebersetzter Name koenne Umlaute auf eine Windows-Freigabe tragen") trifft fuer den aktuellen deutschen Katalogwert nicht zu (kein Umlaut). Die Sicherheit haengt darauf aber NICHT: zip-filename.ts schneidet jeden Namen unabhaengig vom Katalogwert auf das fuer Windows Zulaessige zurueck (verbotene Zeichen, Steuerzeichen, Nicht-ASCII, abschliessende Punkte/Leerzeichen, reservierte Geraetenamen, Ersatzname bei leerem Ergebnis, Endung sichergestellt) — elf Testfaelle belegen das einzeln.

  2. noUselessSwitchCase in tender-normalizer.service.ts. Die Fallmarke case 'doe-opendata': unmittelbar ueber default: war ueberfluessig — entfernt, der erweiterte Kommentar traegt jetzt beide Aussagen (DÖE-Quelle landet hier UND kuenftige additive SourceType-Mitglieder sollen ebenfalls hier landen). Kein Verhaltenswechsel, belegt durch die 18 vorhandenen Tests.

  3. Doppeltes Kalender-Ladefenster. Trat nur bei der Vorschau-Einstellung 90 Tage auf (nicht bei der Voreinstellung 30). Der eingefrorene Testzeitpunkt (15.07.2026) zeigt die Kollision zwischen August und September — eine Abweichung vom im PLAN genannten Monatspaar Oktober/November (derselbe Mechanismus, ein anderes "heute"; gleiche Art Abweichung wie bereits in Test 8 fuer den 30-Tage-Fall dokumentiert). lastFetchWindowRef ueberspringt den Termin-Abruf bei uebereinstimmendem Fenster; der 5-Minuten-Auffrischer umgeht den Sperrgriff immer.

  4. Die vier t-Abhaengigkeiten. Die gof-Annahme "t ist bei jedem Render frisch" ist ausdruecklich WIDERLEGT: translations-identity.test.tsx beweist per Referenzgleichheit (nicht nur toEqual) an einem echten NextIntlClientProvider, dass t bei einem lokalen Zustandswechsel dasselbe Funktionsobjekt bleibt. Bestaetigt durch den use-intl@4.13.0-Quelltext: translate entsteht in einem useMemo, dessen Abhaengigkeiten ausschliesslich aus dem root-staendigen Intl-Kontext stammen. Die vier verbliebenen Stellen (marketplace/page.tsx, admin/users/page.tsx, calendar-settings-panel.tsx, calendar-source-form.tsx) bleiben deshalb unveraendert — zwei davon haengen ohnehin an keinem Effekt (Ereignisbehandlung), die beiden anderen wurden in 260921-gof bereits korrekt behandelt. Die Faustregel "t gehoert in keine Abhaengigkeitsliste" bleibt als Konvention in Ordnung, die zugrunde liegende Begruendung ist jetzt korrigiert.

Ehrliche Einordnung: die vier aria-hidden-Ergaenzungen

Alle vier <img onError>-Stellen (header.tsx, account-settings-form.tsx, favorites-widget.tsx zweimal) tragen bereits alt="" — sind also schon aus dem Zugaenglichkeitsbaum genommen. aria-hidden="true" sagt dasselbe nur ausdruecklich. Es ist richtige Auszeichnung, aber es verbessert fuer keinen Menschen etwas — der Befund verschwindet, weil die Regel ein verborgenes Element nicht mehr betrachtet. onError ist ein Ladefehler, keine Bedienung: hier gab es nie einen Tastaturweg zu schaffen. Keine Unterdrueckung, kein biome-ignore.

Vorher/Nachher (Biome-JSON, nach Regel)

Zeitpunkt gesamt a11y (5 zurueckgestellte Regeln) errors
Vor diesem Vorgang (Baseline, vom Planer gemessen) 429 30 0
Nach Aufgabe 1 (Struktur-Umbauten) 413 14 (5 = 4 <img onError> + Taschenrechner-Rahmen der Klick-Regeln; useAriaPropsSupportedByRole noch 5, noAutofocus noch 4) 0
Nach Aufgabe 2 (Rollen/Beschriftungen/Autofokus/Restposten 1+2) 399 1 0
Nach Aufgabe 3 (Kalender-Abrufe, t-Identitaet — keine a11y-Aenderung) 399 1 0

Endstand deckt sich exakt mit dem Zielwert des Plans: gesamt 399, a11y 1 (calculator-widget.tsx, noNoninteractiveElementInteractions, bewusst stehengelassen), switch 0, errors 0. Keine neue biome-ignore-Zeile im gesamten Diff (git diff --unified=0 7557c9a.. | grep -c biome-ignore → 0). Beide Kataloge nach wie vor gleich lang (912/912), keine Datei nur in einem Katalog.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug, waehrend der Arbeit selbst erkannt und vor dem Commit korrigiert] Regex mit Steuerzeichen-Bereich loeste eine neue Lint-Regel aus

  • Found during: Aufgabe 2, Umsetzung der ZIP-Namens-Schutzfunktion
  • Issue: Die urspruengliche Fassung von zip-filename.ts nutzte /[<>:"/\\|?*\x00-\x1f]/g — ein \x00-\x1F-Bereich in einem Regex-Literal, den Biome (lint/suspicious/noControlCharactersInRegex) beanstandet, auch wenn die Absicht (Steuerzeichen ausfiltern) hier keine ist. Der Test-Datei-Umbau selbst zog dabei ausserdem lint/style/useTemplate (info) und suppressions/unused (ein ueberfluessiger biome-ignore-Kommentar) nach sich.
  • Fix: Steuerzeichen-/Sonderzeichen-Filterung auf zeichenweise Iteration umgestellt (kein Regex fuer diesen Teil); String-Verkettung im Test durch ein Template-Literal ersetzt; den ueberfluessigen biome-ignore entfernt.
  • Files modified: apps/web/src/app/(portal)/modules/cert-manager/zip-filename.ts, apps/web/src/app/(portal)/modules/cert-manager/zip-filename.test.ts
  • Verification: Volle Regelmessung danach zeigt gesamt 399 (Zielwert exakt getroffen), 0 Fehler, 0 unbenutzte Unterdrueckungen.
  • Committed in: 6c10c9b (im selben Commit korrigiert, nie mit dem Fund committet)

2. [Rule 1 - Bug, vor dem Commit korrigiert] Write-Werkzeug wandelte \u0007 still in ein rohes Steuerzeichen um

  • Found during: Aufgabe 2, Anlegen von zip-filename.test.ts
  • Issue: Der bekannte Fallstrick aus dem Plan (\uXXXX-Folgen werden vom Write-Werkzeug in das tatsaechliche Zeichen umgewandelt) trat ein weiteres Mal ein — ein rohes BEL-Steuerzeichen (0x07) landete zwischen zwei Buchstaben im Testtext, sichtbar erst per cat -A.
  • Fix: Die betroffene Zeile per python3-Skript (Bytesuche/-ersatz, chr(7)) auf String.fromCharCode(7) innerhalb eines Template-Literals umgestellt — erzeugt das Zeichen zur Laufzeit, keine \u-Folge mehr im Dateiinhalt. Alle Dateien dieses Vorgangs danach auf verbliebene rohe Steuerbytes durchsucht (0 Treffer).
  • Files modified: apps/web/src/app/(portal)/modules/cert-manager/zip-filename.test.ts
  • Verification: cat -A zeigt keine Steuerbyte-Artefakte mehr; git commit nahm die Datei ohne Beanstandung an.
  • Committed in: 6c10c9b (vor dem Commit korrigiert)

3. [Rule 3 - Blocking, vor dem Commit korrigiert] userEvent-Tastaturtest haengt mit Fake-Timern

  • Found during: Aufgabe 1, Testerweiterung fuer das Notiz-Kaestchen
  • Issue: Ein Test mit userEvent.keyboard(' ') haengte (5s Timeout) unter den in beforeEach global gesetzten Fake-Timern (vi.useFakeTimers()), auch mit advanceTimers-Option.
  • Fix: Fuer diesen einen Test vi.useRealTimers() gesetzt (das Abhaken speichert sofort ohne Entprellen, echte Zeitgeber sind dafuer unproblematisch); afterEach stellt ohnehin vi.useRealTimers() global sicher.
  • Files modified: apps/web/src/components/dashboard/widgets/note-widget.test.tsx
  • Verification: Test gruen, restliche 10 Tests der Datei unbeeinflusst.
  • Committed in: b601141

Total deviations: 3 auto-fixed (2 Bugs waehrend eigener Arbeit erkannt und vor dem jeweiligen Commit korrigiert, 1 blockierendes Test-Infrastruktur-Problem). Keine der drei hat den Endstand oder den Diff-Umfang ueber das im Plan Vorgesehene hinaus vergroessert — alle drei sind Korrekturen an waehrend dieses Vorgangs selbst neu geschriebenem Code, kein Scope-Creep in bestehende Dateien.

Issues Encountered

Keine ungeloesten Probleme. Die drei oben dokumentierten Abweichungen sind vor dem jeweiligen Commit vollstaendig geloest.

Known Stubs

Keine. Alle Aenderungen sind vollstaendige, funktionierende Korrekturen; keine Platzhalter, keine leeren Datenquellen.

Threat Flags

Keine neue Angriffsflaeche. Der Umbau des Loeschdialog-Hintergrunds (calendar-settings-panel.tsx) und des Dialog-Hintergrunds (widget-catalog-modal.tsx) macht ein zuvor rein optisches Element zu einem fokussierbaren <button> — das ist die im threat_model als accept eingestufte Informationsfreigabe T-JT4-06 (Dialoginhalt war ohnehin sichtbar), keine neue Flag.

User Setup Required

None — keine externe Konfiguration erforderlich.

Was an den Orchestrator geht (kein Browser hier verfuegbar)

Der Ausfuehrende hat keinen Browser und darf die Browser-Messung aus Aufgabe 3 nicht behaupten. An den Orchestrator zu uebergeben:

  • Kalender-Widget, Netzwerkprotokoll bei 90 Tagen Vorschau: Stack bauen, anmelden, Kalender-Kachel mit Vorschau-Einstellung 90 Tage aufs Dashboard legen, Netzwerkprotokoll leeren, dreimal "Weiter" druecken.
    • Erwartung: calendar/sources genau 1 (vorher 3); calendar/events weniger als 3, kein Paar mit identischem from/to.
    • Gegenprobe: rund fuenf Minuten ruhen lassen, danach je ein weiterer calendar/events- und calendar/sources-Abruf.
    • Datenbank/Modul-Aktivierung danach unveraendert lassen, Stack stoppen.

Alle anderen Verifikationsschritte des Plans (Biome-Zaehlung, Katalog-Gleichstand, apps/web/apps/api-Testlaeufe, pnpm type-check, pnpm lint --force, git diff-Kontrollen) wurden hier bereits ausgefuehrt und sind oben mit Ergebnissen belegt.

Next Phase Readiness

Der a11y-Rueckstand aus 260921-bi2 (30 zurueckgestellte Befunde ueber fuenf Regeln) ist vollstaendig abgearbeitet: 29 behoben, 1 bewusst und begruendet stehengelassen. Alle vier vom Nutzer/Orchestrator benannten Restposten aus den heutigen Vorgaengen bi2 und gof sind geschlossen. Kein Blocker fuer laufenden Betrieb: pnpm lint --force bleibt gruen (5/5), CI-Tor unveraendert scharf. Offen bleibt ausschliesslich die Browser-Gegenprobe fuer die Kalender-Netzwerkzaehlung (siehe Abschnitt oben) — reine Bestaetigung, kein bekannter Defekt.

Self-Check: PASSED

Alle referenzierten Dateien auf Datentraeger gefunden (zip-filename.ts, zip-filename.test.ts, translations-identity.test.tsx, widget-catalog-modal.test.tsx, calendar-settings-panel.test.tsx, diese SUMMARY). Alle referenzierten Commit-Hashes (a8531d4, 3d0bc0b, 0c89c13, b601141, e651c24, 9aa87bd, b406a9c, f7b5df4, 69fe706, 5a03b75, 6c10c9b, f471b78) im Verlauf gefunden (git log --oneline 7557c9a..HEAD).


Vorgang: quick-260921-jt4 Abgeschlossen: 2026-09-21