- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
apps/web+packages, noUselessEscapeInRegex, useConst,
useExponentiationOperator) sowie fuenf ungesicherte Regeln
(useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
(ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
noUnusedImports/noUnusedFunctionParameters zu erreichen
Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.
Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
der Seite stehen (keine Weiterleitung, keine Aktualisierung der
Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
The doe-opendata adapter stored the OCDS document's own `uri` as sourceUrl.
That is the API address of the record and serves OCDS JSON by design, so
anyone following the link from the results list, the detail view, or an alert
mail landed on raw JSON instead of the notice.
Build the human-readable page from the notice id the row already carries
instead. `/ui/de/search/details?noticeId=...` is the redirect target of
`/ui/de/notices/...`, so it needs no redirect. Verified in a browser for both
id shapes the feed uses -- numeric (25673764 -> "Feuerwehr-Geraetehaus Miehlen
Fliesenarbeiten") and UUID (7085ba12-... -> "Holzfassade"). The page is a
single-page app that answers 200 with an identical shell for any id, so this
had to be checked on rendered content; a status code proves nothing.
The adapter alone only fixes new ingests, so a backfill migration rewrites the
rows already stored -- in Tender and in TenderSource, since the detail view
lists per-source links separately. It touches only rows still pointing at
/api/notices/ and only ids of a shape that was actually verified, which makes
it idempotent and keeps an unexpected id from being pasted into a URL. Counted
read-only against the live database beforehand: 2846 DOE rows affected, none
skipped.
Closes the 2026-08-05 backlog item, which was deliberately held until Phase 16
was done.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>