Files
tessera-ctl/.planning/quick/260921-bi2-lint-rueckstand-abbauen-mechanische-fixe/260921-bi2-VERIFICATION.md
T
schalli 116041b7fd
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 1m9s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 18s
Tessera CI/CD / Build & Publish Images (push) Successful in 2m51s
docs(quick-260921-bi2): gemeldetes Symptom Passwortwechsel widerlegt
Der Lint-Durchlauf meldete, die Seite change-password leite nach
erfolgreichem Wechsel nicht weiter und lasse die Person bei erzwungenem
Wechsel stehen. Am laufenden System durchgespielt: trifft nicht zu.
Middleware leitet auf /change-password, der Wechsel landet auf /,
mustChangePassword steht danach auf false, Weiternavigieren geht.

changePasswordAction setzt das neue Sitzungs-Cookie und ruft redirect('/')
serverseitig; die ungenutzten router/setUser im Seitenmodul waren
Ueberbleibsel, kein Symptom. Entfernung des toten Codes war richtig,
die daraus abgeleitete Diagnose nicht.

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

142 lines
21 KiB
Markdown

---
phase: quick-260921-bi2
verified: 2026-09-21T09:47:00Z
status: passed
score: 9/9 must-haves verified
covered_files: [".planning/quick/260921-bi2-lint-rueckstand-abbauen-mechanische-fixe/260921-bi2-PLAN.md",".planning/quick/260921-bi2-lint-rueckstand-abbauen-mechanische-fixe/260921-bi2-SUMMARY.md","apps/api/scripts/rls-scratch-check.mjs","apps/api/src/auth/auth.service.ts","apps/api/src/auth/decorators/current-user.decorator.ts","apps/api/src/auth/interceptors/force-password-change.interceptor.ts","apps/api/src/auth/strategies/jwt.strategy.ts","apps/api/src/calendar/calendar.service.ts","apps/api/src/calendar/dto/create-calendar-source.dto.ts","apps/api/src/calendar/providers/caldav.provider.ts","apps/api/src/calendar/providers/exchange.provider.ts","apps/api/src/calendar/providers/ics.provider.ts","apps/api/src/cert-manager/cert-manager.service.spec.ts","apps/api/src/cert-manager/cert-manager.service.ts","apps/api/src/crypto/crypto.service.ts","apps/api/src/desktop/desktop.service.spec.ts","apps/api/src/desktop/desktop.service.ts","apps/api/src/dkv/dkv-export.service.ts","apps/api/src/dkv/dkv-parser.service.ts","apps/api/src/dkv/dkv-parser.validate.ts","apps/api/src/dkv/dkv.controller.ts","apps/api/src/dkv/dkv.service.spec.ts","apps/api/src/dkv/dkv.service.ts","apps/api/src/favorites/icon-discovery.service.ts","apps/api/src/groups/groups.service.spec.ts","apps/api/src/groups/groups.service.ts","apps/api/src/inbox/imap.provider.spec.ts","apps/api/src/ldap/ldap-config.service.spec.ts","apps/api/src/ldap/ldap.service.spec.ts","apps/api/src/ldap/ldap.service.ts","apps/api/src/prisma/prisma-tenant.extension.spec.ts","apps/api/src/tenders/adapters/cosinex.adapter.spec.ts","apps/api/src/tenders/adapters/doe-opendata.adapter.spec.ts","apps/api/src/tenders/adapters/email-alert.adapter.spec.ts","apps/api/src/tenders/adapters/email-alert.adapter.ts","apps/api/src/tenders/adapters/netserver.adapter.spec.ts","apps/api/src/tenders/adapters/rss.adapter.spec.ts","apps/api/src/tenders/adapters/rss.adapter.ts","apps/api/src/tenders/tender-digest.scheduler.spec.ts","apps/api/src/tenders/tender-digest.scheduler.ts","apps/api/src/tenders/tender-fingerprint.ts","apps/api/src/tenders/tender-ingestion.service.spec.ts","apps/api/src/tenders/tender-matching.service.ts","apps/api/src/tenders/tender-normalizer.service.spec.ts","apps/api/src/tenders/tender-normalizer.service.ts","apps/api/src/tenders/tender-rss-feed.service.spec.ts","apps/api/src/tenders/tender-scheduler.service.spec.ts","apps/api/src/user/user.controller.ts","apps/desktop/src/setup.html","apps/web/src/app/(auth)/login/page.tsx","apps/web/src/app/(auth)/reset-password/[token]/page.tsx","apps/web/src/app/(auth)/reset-password/page.tsx","apps/web/src/app/(portal)/admin/groups/components/DeleteGroupDialog.tsx","apps/web/src/app/(portal)/admin/groups/components/GroupFormModal.tsx","apps/web/src/app/(portal)/admin/groups/page.tsx","apps/web/src/app/(portal)/admin/layout.tsx","apps/web/src/app/(portal)/admin/ldap/page.tsx","apps/web/src/app/(portal)/admin/modules/page.tsx","apps/web/src/app/(portal)/admin/tenants/page.tsx","apps/web/src/app/(portal)/admin/users/page.tsx","apps/web/src/app/(portal)/change-password/page.tsx","apps/web/src/app/(portal)/marketplace/[slug]/page.tsx","apps/web/src/app/(portal)/marketplace/components/ActivationDialog.tsx","apps/web/src/app/(portal)/marketplace/components/CategoryFilter.tsx","apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx","apps/web/src/app/(portal)/marketplace/components/MarketplaceSearch.tsx","apps/web/src/app/(portal)/marketplace/components/StatusFilter.tsx","apps/web/src/app/(portal)/marketplace/components/TenantContextSelector.tsx","apps/web/src/app/(portal)/marketplace/components/Toast.tsx","apps/web/src/app/(portal)/marketplace/page.tsx","apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/module-shell.tsx","apps/web/src/app/(portal)/modules/[category]/components/ModuleCard.tsx","apps/web/src/app/(portal)/modules/[category]/page.tsx","apps/web/src/app/(portal)/modules/cert-manager/cert-manager.test.tsx","apps/web/src/app/(portal)/modules/cert-manager/components/ConvertTab.tsx","apps/web/src/app/(portal)/modules/cert-manager/components/DropZone.tsx","apps/web/src/app/(portal)/modules/cert-manager/components/InspectTab.tsx","apps/web/src/app/(portal)/modules/cert-manager/components/PasswordField.tsx","apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx","apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx","apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx","apps/web/src/app/(portal)/page.tsx","apps/web/src/app/(portal)/settings/layout.tsx","apps/web/src/app/icon.svg","apps/web/src/components/admin/admin-sidebar.tsx","apps/web/src/components/dashboard/edit-mode-toggle.tsx","apps/web/src/components/dashboard/widget-catalog-modal.tsx","apps/web/src/components/dashboard/widget-registry.tsx","apps/web/src/components/dashboard/widgets/calculator-widget.tsx","apps/web/src/components/dashboard/widgets/clock-widget.tsx","apps/web/src/components/dashboard/widgets/favorites-widget.tsx","apps/web/src/components/dashboard/widgets/note-widget.tsx","apps/web/src/components/dashboard/widgets/search-widget.tsx","apps/web/src/components/dashboard/widgets/widget-wrapper.tsx","apps/web/src/components/layout/header.tsx","apps/web/src/components/layout/sidebar-footer.tsx","apps/web/src/components/layout/sidebar.tsx","apps/web/src/components/locale-switcher.tsx","apps/web/src/components/modules/module-access-denied.tsx","apps/web/src/components/settings/account-settings-form.tsx","apps/web/src/components/settings/calendar-settings-panel.tsx","apps/web/src/components/settings/calendar-source-form.tsx","apps/web/src/components/settings/search-provider-form.tsx","apps/web/src/components/settings/settings-sidebar.tsx","apps/web/src/components/settings/widget-settings-panel.tsx","apps/web/src/components/theme-toggle.tsx","apps/web/src/lib/auth-actions.ts","apps/web/src/messages/de.json","apps/web/src/messages/en.json","apps/web/src/middleware.ts","biome.json","docs/anleitung-entwicklung.md"]
covered_digest: "v1:sha256:d7552864f2e6d3eacbdd144ddac34117636e8ccb23da588ae1eef353463901b8"
behavior_unverified: 1
overrides_applied: 0
behavior_unverified_items:
- truth: "In den drei Formularseiten der Verwaltung (Mandanten, Benutzer, LDAP) sendet der echte Absendeknopf weiterhin ab, und ein Klick auf Abbrechen/Schliessen loest KEIN Absenden mehr aus (useButtonType-Korrektur, 52 Fundstellen)."
test: "pnpm dev starten (nicht gegen den Testserver), die drei Seiten Mandanten/Benutzer/LDAP oeffnen, je Formular den Absendeknopf klicken (muss abschicken) und Abbrechen/Schliessen klicken (darf NICHT abschicken)."
expected: "Absenden funktioniert weiterhin unveraendert; jede Nebenschaltflaeche (Abbrechen, Schliessen, Zeilenaktionen) sendet das umgebende Formular nicht mehr ab."
why_human: "Statische Zaehlung (grep auf type=\"submit\"/<form>) bestaetigt, dass die Struktur unveraendert ist (1/1/2) und kein type=\"submit\" entfernt wurde -- das beweist aber nicht das Klick-Verhalten im Browser selbst. Dies ist genau der im PLAN.md-<human-check>-Block (Aufgabe 3) explizit deferierte Test."
---
# Quick-Vorgang 260921-bi2: Lint-Rueckstand abbauen (mechanische Fixe) Verification Report
**Vorgangsziel:** Lint-Rueckstand abbauen — mechanische Fixe, toter Code, Barrierefreiheit; Test-Mock-Warnungen begruendet ausnehmen. Ohne Verhaltenswechsel.
**Verifiziert:** 2026-09-21
**Status:** human_needed
**Re-Verifikation:** Nein — Erstverifikation
## Zusammenfassung
Alle neun `must_haves.truths` aus dem PLAN sind gegen den tatsaechlichen Code nachgewiesen, nicht nur gegen die SUMMARY-Behauptung. Der einzige offene Punkt ist ein vom Plan selbst deferierter menschlicher Bedienungstest (Klickverhalten der drei Verwaltungsformulare) — das ist der Grund fuer `human_needed` statt `passed`. Kein einziger automatisierter Befund ist offen.
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | Lint-Rueckstand faellt von 2923 auf ~466 (rund), 0 Fehler, CI-Lint bleibt gruen | ✓ VERIFIED | Frisch gemessen: `total 465, real 386, test 79, errors 0`. `pnpm lint --force` 5/5 erfolgreich. |
| 2 | Testdatei-Ausnahme reicht nicht in echten Quelltext: exakt N `noExplicitAny` in echtem Quelltext, 0 in Testdateien | ✓ VERIFIED (mit dokumentierter Zahlenabweichung) | Frisch gemessen: 288 real / 0 test. Plan-Text nannte 289; SUMMARY erklaert die Differenz von 1 ehrlich als inzidentellen Nebeneffekt einer `catch(e: any)`-Entfernung in Aufgabe 2 — nicht als verdeckte Anpassung. Die eigentliche Eigenschaft der Wahrheit (0 in Testdateien, Ausnahme leckt nicht in Produktivcode) haelt exakt. |
| 3 | NestJS-`__metadata`-Abdruck vor/nach dem maschinellen Durchgang Zeichen-fuer-Zeichen identisch (593 Zeilen, sha256 `6e1583f1...`) | ✓ VERIFIED | Selbst nachgestellt: `593` Zeilen, sha256 `6e1583f1eb72a089eb0ed98f81158b54a9fbd40dbf41371292725f36ef764300` — exakter Treffer. Zusaetzlich stichprobenweise `auth.service.js`, `ldap.service.js`, `ldap-config.service.js`, `calendar.service.js` gelesen: Konstruktor-Parametertypen sind echte Klassenreferenzen (`PrismaService`, `JwtService`, `UserService`, `GroupsService`, `CryptoService`, ...), nirgends `Function`. |
| 4 | Beide Testlaeufe punktgleich gruen, `pnpm type-check` 4/4 | ✓ VERIFIED | `apps/api`: 69 Dateien / 1124 Tests gruen. `apps/web`: 66 Dateien / 459 Tests gruen. `pnpm type-check`: 4/4 (Turbo FULL TURBO, alle 4 Pakete erfolgreich). |
| 5 | Regelgruppe `security` unveraendert auf `error`, nirgends erwaehnt | ✓ VERIFIED | `biome.json` per Skript geprueft: `linter.rules.a11y === "warn"`, `overrides.length === 2`, String `"security"` kommt in der Datei nirgends vor. |
| 6 | Alle 155 Barrierefreiheits-Befunde der sechs bearbeiteten Regeln auf 0, keine a11y-Regel herabgestuft/abgeschaltet | ✓ VERIFIED | Frisch gemessen: `noSvgWithoutTitle=0, useButtonType=0, noLabelWithoutControl=0, useAriaPropsSupportedByRole=0` (Anm.: diese Regel ist laut aktuellem Plan-Scope zurueckgestellt, siehe unten — Zahl 5 bestaetigt), `noRedundantRoles=0, useAriaPropsForRole=0`. Die sechs zurueckgestellten Regeln unveraendert: `noNoninteractiveElementInteractions=11, useKeyWithClickEvents=5, noStaticElementInteractions=5, useAriaPropsSupportedByRole=5, noAutofocus=4, noUselessSwitchCase=1`. `a11y` bleibt gruppenweit auf `warn`, keine Einzelausnahme in `overrides`. |
| 7 | Absendende Schaltflaechen in Mandanten/Benutzer/LDAP unveraendert bei 1/1/2 | ✓ VERIFIED (strukturell) — Klickverhalten siehe Human Verification | `grep -c 'type="submit"'` / `grep -c '<form'`: tenants 1/1, users 1/1, ldap 2/2 — exakter Treffer. Kein `type="submit"` wurde im gesamten Diff entfernt (`git diff | grep '^-.*type="submit"'` liefert nichts); 54 `type="button"` wurden ergaenzt. Die Struktur ist beweisbar unveraendert; das tatsaechliche Klickverhalten im Browser ist ein von Aufgabe 3 selbst als `<human-check>` deferierter Test (siehe unten). |
| 8 | Keine repo-weite Formatierung, zeilenbilanzierter Diff | ✓ VERIFIED | `git diff --stat e7c2c4c..HEAD`: 110 Dateien, 435 Einfuegungen / 260 Loeschungen, kein einzelner Datei-Diff ueber 75 Zeilen (groesste: `DropZone.tsx` 75, `ldap.service.ts` 50) — kein Muster einer Vollumschreibung. `pnpm-lock.yaml` und alle `package.json`-Versionsfelder unveraendert. |
| 9 | Entwickleranleitung nennt neuen Stand, beide Ausnahmen begruendet, Folgeaufgaben benannt | ✓ VERIFIED | `docs/anleitung-entwicklung.md` gelesen: nennt 465/386/79, beide `overrides`-Begruendungen in ganzen Saetzen, die sechs zurueckgestellten Regelklassen und die vier D-03-Symptomfunde als Folgeaufgaben. |
**Score:** 9/9 truths verified (1 present, behavior-unverified — siehe Human Verification)
### Required Artifacts
| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `biome.json` | genau 2 `overrides`, `security` unberuehrt, Fixture-Ausnahme korrigiert | ✓ VERIFIED | 2 Eintraege (`**/*.spec.ts` etc. → `noExplicitAny: off`; `apps/api/**` → `useImportType: off`); `files.includes` enthaelt `!**/__fixtures__` ohne Doppelstern. |
| `docs/anleitung-entwicklung.md` | neuer Stand + Begruendungen | ✓ VERIFIED | Siehe Truth 9. |
| 45 maschinelle Korrekturdateien (Aufgabe 2) | gelesen, zeilenbilanziert | ✓ VERIFIED | `__metadata`-Fingerabdruck unveraendert; kein Formatierungs-Ueberschuss im Gesamtdiff. |
| 15 Fundstellen toter Code | 10 entfernt, 1 umbenannt, 4 gemeldet | ✓ VERIFIED | `current-user.decorator.ts` (`data`→`_data`) und `force-password-change.interceptor.ts` (Variable `method` entfernt) stichprobenweise gelesen — exakt wie in SUMMARY beschrieben. |
| 53 Dateien mit a11y-Korrekturen | 155 Fundstellen behoben | ✓ VERIFIED | Siehe Truth 6; Stichproben unten. |
### Key Link Verification
| From | To | Via | Status | Details |
|------|-----|-----|--------|---------|
| `apps/api/tsconfig.json` (`emitDecoratorMetadata`) | erzeugtes `__metadata(design:paramtypes)` | `tsc`-Kompilat | ✓ WIRED | Fingerabdruck- und Stichprobenpruefung bestaetigen: Werte bleiben echte Klassenreferenzen. |
| `biome.json overrides[0].includes` | `noExplicitAny`-Ausnahme | Musterabgleich | ✓ WIRED | 0 Treffer in Testdateien, 288 in echtem Quelltext — Ausnahme wirkt nur auf die vier Testmuster. |
| Formularseiten (Mandanten/Benutzer/LDAP) | `type="submit"`-Knopf | statische Struktur | ✓ WIRED (strukturell) | 1/1/2 bestaetigt; Klickverhalten selbst siehe Human Verification. |
### Anti-Patterns Found
Keine Befunde in den geaenderten Dateien. Alle drei `XXX`-Treffer in `cert-manager.test.tsx` sind Platzhalter-PEM-Zertifikatsinhalt in Test-Fixtures, keine Debt-Marker. Keine `TODO`/`FIXME`/`HACK`/`PLACEHOLDER`-Marker, keine leeren Ruecksprunge, keine hartcodierten leeren Props im Diff.
### Behavioral Spot-Checks
| Behavior | Command | Result | Status |
|----------|---------|--------|--------|
| NestJS-Metadaten unveraendert | `tsc --outDir` + `grep __metadata` + `sha256sum` | 593 Zeilen, `6e1583f1...` | ✓ PASS |
| Konkrete Klassenreferenzen statt `Function` | `grep -A3 __metadata` in `auth.service.js`, `ldap.service.js`, `calendar.service.js` | `PrismaService`, `JwtService`, `UserService`, `GroupsService`, `CryptoService`, ... | ✓ PASS |
| `apps/api` Vitest | `pnpm --filter @tessera/api run test` | 69/1124 gruen | ✓ PASS |
| `apps/web` Vitest | `pnpm --filter @tessera/web run test` | 66/459 gruen | ✓ PASS |
| `pnpm type-check` | `pnpm type-check` | 4/4 erfolgreich | ✓ PASS |
| `pnpm lint --force` | `pnpm lint --force` | 5/5 erfolgreich, 0 Fehler | ✓ PASS |
| i18n-Schluesselparitaet | Node-Skript ueber `de.json`/`en.json` | 892/892, 0 Differenz | ✓ PASS |
| Klickverhalten der drei Formulare im Browser | — | nicht ausgefuehrt | ? SKIP (siehe Human Verification) |
### Manuelle Code-Lesung (a11y-Diff, Stichprobe)
Auftrag verlangte, uebers Gruen der Testlaeufe hinaus eine Stichprobe des 155-Fundstellen-Diffs selbst zu lesen und gezielt nach drei Fehlerbildern zu suchen:
- **Symbol faelschlich `aria-hidden`, obwohl es die einzige Beschriftung der Bedienung ist:** Keinen Fall gefunden. Stichprobe ueber die neun Dateien mit den meisten `aria-hidden`-Ergaenzungen (`widget-registry.tsx`, `sidebar.tsx`, `admin-sidebar.tsx`, `calendar-settings-panel.tsx`, `header.tsx`, `theme-toggle.tsx`, `Toast.tsx`, `setup.html`, `icon.svg`) zeigt durchgehend: Symbol entweder neben sichtbarem Text (`{item.label}`, `{t('dashboard')}` als bedingt gerenderter `<span>`) oder die umgebende Schaltflaeche/der Link traegt ein eigenes `aria-label`. Der einzige alleinstehende Fall (Standardzuordnungs-Schloss-Symbol in `admin/ldap/page.tsx`, Tabellenzelle ohne begleitenden Text) bekam korrekt ein `<title>{t('fieldMapping.defaultIcon')}</title>` statt `aria-hidden`. Zaehlkontrolle: 69 `aria-hidden`-Ergaenzungen + 2 `<title>`-Ergaenzungen (`icon.svg`, `fieldMapping.defaultIcon`) = 71, exakt die Zahl der `noSvgWithoutTitle`-Fundstellen.
- **`<label>` durch `<span>` "reparieren"" statt Bindung herzustellen:** Keinen Fall gefunden. `git diff | grep -B3 '^-.*<label'` zeigt keinen einzigen Fall, in dem eine geloeschte `<label>`-Zeile durch eine neue `<span>`-Zeile ersetzt wurde. Alle 22 `noLabelWithoutControl`-Fixe binden stattdessen `htmlFor`/`id` (Stichprobe: `group-name`, `tenant-name`, `tenant-slug`, `user-username`, `user-email`, `user-password`, `ldap-bind-password`, `ldap-search-filter`, `ldap-sync-interval`, `ldap-mapping-ldap-field`, `ldap-mapping-tessera-field`, `ldap-group-manual-dn`, `ldap-exclude-username`, `ldap-server-url`, `ldap-base-dn`, `ldap-bind-dn`, `group-internal-name` — je genau ein `label htmlFor=` und ein `input id=` desselben Namens, keine Kollisionen). Sonderfall Farbauswahl in `calendar-source-form.tsx`: `label`→`fieldset`/`legend` fuer eine Gruppe von Farb-Schaltflaechen (kein Einzelfeld) — sachlich korrekt, keine Assoziation entfernt.
- **`type="button"` faelschlich auf dem echten Absendeknopf:** Keinen Fall gefunden. 54 `type="button"`-Ergaenzungen im Diff, 0 `type="submit"`-Entfernungen; die drei betroffenen Formularseiten behalten ihre 1/1/2-Absendeknopf-Zaehlung exakt bei.
`DropZone.tsx` (SUMMARY-Abweichung) gelesen: Die Drop-Flaeche ist jetzt selbst ein `<button type="button">` mit Klick- UND Drag-Handlern (`onDragOver`/`onDragLeave`/`onDrop`), die "Entfernen"-Schaltflaeche liegt als `position: absolute`-Geschwister in einem umgebenden `<div className="relative">`, nicht mehr verschachtelt — kein `<button>` enthaelt ein zweites `<button>` (gueltiges Markup). Native Drag-Ereignisse sind an jedem HTML-Element inklusive `<button>` verfuegbar; die Umstellung aendert am Drag-and-Drop-Verhalten nichts. Die Begruendung im Kommentar der Datei selbst deckt sich mit dem Code. Urteil: die Abweichung ist sauber und loest das beschriebene Problem korrekt.
### Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
|-------------|-------------|-------------|--------|----------|
| LINT-BACKLOG | 260921-bi2-PLAN.md | Lint-Rueckstand abbauen ohne Verhaltenswechsel | ✓ SATISFIED | Siehe alle Truths oben. Kein Eintrag in `.planning/REQUIREMENTS.md` unter dieser ID — Quick-Vorgang, kein Meilenstein-Requirement-Tracking erwartet. |
## Human Verification Required
### 1. Klickverhalten der drei Verwaltungsformulare (Mandanten, Benutzer, LDAP)
**Test:** `pnpm dev` starten (nicht gegen den Testserver messen). Auf jeder der drei Seiten — `/admin/tenants`, `/admin/users`, `/admin/ldap` — den echten Absendeknopf klicken und zusaetzlich jede Nebenschaltflaeche (Abbrechen, Schliessen, Zeilenaktionen) klicken.
**Expected:** Der echte Absendeknopf sendet das Formular weiterhin ab. Keine Nebenschaltflaeche loest ein Absenden aus.
**Why human:** Die statische Struktur (Zaehlung `type="submit"`/`<form>`, keine geloeschte Submit-Zeile) ist zweifelsfrei nachgewiesen und belegt, dass sich am Absende-Markup nichts geaendert hat. Sie beweist aber nicht das tatsaechliche Klickverhalten im Browser — genau das ist der Zweck der `useButtonType`-Korrektur (52 Fundstellen), und PLAN.md selbst deferiert diesen Test explizit in einem `<human-check>`-Block fuer Aufgabe 3, weil "ein gruener Testlauf davon nur einen Teil abdeckt".
## Gaps Summary
Keine Gaps. Alle automatisierten Nachweise — Fingerabdruck, Zaehlungen, Testlaeufe, Typpruefung, Lint-Tor, i18n-Paritaet, Diff-Zeilenbilanz, Konfigurationsform — sind gegen den tatsaechlichen Code nachgestellt und bestehen exakt. Der Status `human_needed` kommt ausschliesslich von dem einen, im Plan selbst als Bedienprobe vorgesehenen Klicktest, der eine Sitzung mit laufendem Dev-Server voraussetzt und nicht durch statische Pruefung ersetzt werden kann.
---
*Verified: 2026-09-21*
*Verifier: Claude (gsd-verifier)*
## Korrektur eines gemeldeten Symptoms (2026-09-21, Orchestrator)
Der Vorgang hat fuenf tote Stellen als "Symptome echter Luecken" gemeldet statt sie zu
reparieren. Eine davon ist bei der Nachpruefung am laufenden System **widerlegt**:
**`apps/web/src/app/(portal)/change-password/page.tsx` — angeblich "leitet nach
erfolgreichem Wechsel weder weiter noch frischt die Benutzerablage auf; bei erzwungenem
Wechsel bleibt die Person auf der Seite stehen".** Das trifft nicht zu.
Gemessen mit den echten Abbildern und Browser, Ablauf vollstaendig durchgespielt:
1. Testbenutzer angelegt, `mustChangePassword=true` gesetzt, angemeldet → die Middleware
(`apps/web/src/middleware.ts:146`) leitet auf `/change-password` um.
2. Wechsel ausgefuellt und abgeschickt → Browser landet auf `/`, das Formular ist weg,
keine Fehlermeldung.
3. Datenbank: `mustChangePassword` steht danach auf `false`.
4. Weiternavigieren auf `/marketplace` funktioniert, kein Zurueckwerfen auf die
Wechselseite; die Kopfzeile zeigt den richtigen Benutzer.
Ursache des Fehlschlusses: `changePasswordAction` (`apps/web/src/lib/auth-actions.ts:160`)
ruft am Ende `redirect('/')` und setzt zuvor das neue Sitzungs-Cookie. Die Weiterleitung
und die Aktualisierung passieren also serverseitig in der Server Action — die im
Seitenmodul ungenutzten `router`/`setUser` waren Ueberbleibsel einer frueheren Loesung,
kein Symptom. Die Entfernung des toten Codes war richtig; die daraus abgeleitete
Fehlerdiagnose war es nicht.
Die uebrigen vier gemeldeten Symptome sind davon unberuehrt und weiterhin ungeprueft —
sie sind Meldungen, keine belegten Fehler, und sollten vor einer Reparatur ebenso am
laufenden System nachgestellt werden.