Files
tessera-ctl/.planning/quick/260921-bi2-lint-rueckstand-abbauen-mechanische-fixe/260921-bi2-PLAN.md
T
schalli e7c2c4c6a9 docs(quick-260921-bi2): Plan fuer den Lint-Rueckstand (2923 -> 466)
Drei Durchgaenge nach Risikoklasse: Konfiguration, maschinelle Fixes
plus toter Code, Barrierefreiheit von Hand. Alle Zielzahlen sind beim
Planen probeweise ausgefuehrt, gemessen und zurueckgenommen worden.

Wichtigster Befund: style/useImportType darf in apps/api nicht
maschinell laufen. emitDecoratorMetadata plus NestJS bedeutet, dass die
Korrektur 61 von 65 Dateien mit Abhaengigkeitsdaten zerstoert — bei
gruenem tsc und gruenen 1124 Tests, weil kein Test den Container
startet. Der Plan begrenzt die Regel auf apps/web und verankert als
Nachweis einen sha256 ueber die erzeugten __metadata-Zeilen.

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

46 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-bi2 01 execute 1
biome.json
docs/anleitung-entwicklung.md
apps/api/src/**
apps/api/scripts/rls-scratch-check.mjs
apps/web/src/**
apps/desktop/src/setup.html
apps/web/src/app/**
apps/web/src/components/**
apps/web/src/app/icon.svg
true
LINT-BACKLOG
tokens raw_tokens tasks confidence
205000 205000 3 low
truths artifacts key_links
Der Lint-Rueckstand faellt von 2923 auf rund 466 Befunde; kein einziger davon hat die Stufe Fehler, der CI-Schritt Lint bleibt gruen (D-07).
Die Ausnahme fuer Testdateien greift nachweislich NICHT in echten Quelltext: nach der Konfigurationsaenderung stehen weiterhin exakt 289 noExplicitAny-Befunde in echtem Quelltext, und 0 in Testdateien (D-01).
Die NestJS-Abhaengigkeitsspritze ueberlebt den maschinellen Durchlauf unversehrt: die aus apps/api erzeugten __metadata-Zeilen sind vor und nach der Aenderung Zeichen fuer Zeichen identisch (593 Zeilen, sha256 6e1583f1...).
Beide Testlaeufe bleiben punktgleich gruen: apps/api 69 Dateien / 1124 Tests, apps/web 66 Dateien / 459 Tests; pnpm type-check bleibt bei 4/4 (D-07).
Die Regelgruppe security steht unveraendert auf error — weder im Regelblock noch in einer Ausnahme wird sie erwaehnt (D-06).
Alle 155 Barrierefreiheits-Befunde der sechs bearbeiteten Regeln sind auf 0; keine a11y-Regel wurde dafuer herabgestuft oder abgeschaltet (D-04, D-06).
In den drei Formularseiten der Verwaltung ist die Zahl der absendenden Schaltflaechen unveraendert (Mandanten 1, Benutzer 1, LDAP 2) — kein Formular hat seinen Absendeknopf verloren, keine Nebenschaltflaeche loest mehr versehentlich ein Absenden aus.
Es wurde keine repo-weite Formatierung angestossen; der Quelltext-Diff ist zeilenbilanziert (D-08).
Die Entwickleranleitung nennt den neuen Stand, beide Ausnahmen mit Begruendung und die namentlich aufgefuehrten Folgeaufgaben.
biome.json — zwei zielgenaue overrides-Eintraege (Testdateien / apps-api) plus die korrigierte Fixture-Ausnahme; security unberuehrt
docs/anleitung-entwicklung.md — Abschnitt zum Lint-Tor auf den neuen Stand gebracht, beide Ausnahmen begruendet, Folgeaufgaben benannt
45 Quelldateien mit maschinell erzeugten, danach von Hand gelesenen mechanischen Korrekturen (Aufgabe 2)
15 Fundstellen toten Codes entfernt bzw. begruendet gemeldet (Aufgabe 2, D-03)
53 Dateien mit handgeschriebenen Barrierefreiheits-Korrekturen (Aufgabe 3, D-04)
Ein Abschlussbericht mit Vorher/Nachher-Zahlen, getrennt nach Testdateien und echtem Quelltext, und der Liste dessen, was bewusst stehen bleibt
biome.json overrides[0].includes -> nur *.spec.ts/*.spec.tsx/*.test.ts/*.test.tsx: die Kette, an der D-01 still zu viel abschalten koennte; Nachweis ist die unveraenderte Zahl 289
emitDecoratorMetadata (apps/api/tsconfig.json) -> __metadata(design:paramtypes) im erzeugten JS -> Nest loest Abhaengigkeiten auf: die Kette, die ein maschinelles import type in apps/api zerreisst; Nachweis ist der sha256-Vergleich
biome lint --only=<regel> -> schaltet eine in der Konfiguration abgeschaltete Regel wieder AN: der Grund, warum useImportType pfadgebunden (apps/web packages) und nie repo-weit laufen darf
<button> ohne Typangabe innerhalb eines <form> -> loest beim Klick ein Absenden aus: die Kette, die useButtonType repariert und die die Zahl der absendenden Schaltflaechen pro Datei belegt
Den Lint-Rueckstand abbauen, den der Vorgang 260921-9ie sichtbar gemacht hat, ohne das Verhalten der Anwendung zu veraendern und ohne eine kosmetische Massenumschreibung.

Ausgangslage, heute frisch gemessen mit biome lint . --reporter=json --max-diagnostics=20000: 2923 Befunde, davon 0 auf der Stufe Fehler — 2067 in Testdateien, 856 in echtem Quelltext.

Der Plan geht in drei Durchgaengen vor, geordnet nach Risikoklasse, nicht nach Dateien:

Durchgang Art der Arbeit Befunde danach (gesamt / echt / Test)
Start — 2923 / 856 / 2067
1 — Konfiguration keine Zeile Quelltext angefasst 754 / 633 / 121
2 — maschinell + toter Code Werkzeug erzeugt, von Hand gelesen 621 / 542 / 79
3 — Barrierefreiheit vollstaendig Handarbeit 466 / 387 / 79

Alle vier Zeilen sind gemessen, nicht geschaetzt: Durchgang 1 und 2 wurden beim Planen vollstaendig probeweise ausgefuehrt, gemessen und danach restlos zurueckgenommen (git restore ., Arbeitsbaum wieder sauber). Die Zahl fuer Durchgang 3 ist die Differenz der sechs bearbeiteten Regeln.

Zwei Befunde aus der Probe, die den Zuschnitt bestimmen — beide belegt, nicht vermutet:

  1. style/useImportType darf in apps/api NICHT maschinell angewendet werden. Es ist mit 224 Befunden die groesste Einzelregel, 222 davon in apps/api. Biome stuft die Korrektur als „sicher" ein, sie ist es hier aber nicht: apps/api/tsconfig.json hat emitDecoratorMetadata eingeschaltet, und NestJS loest seine Abhaengigkeiten ueber genau diese erzeugten Daten auf. Probelauf an auth.service.ts: vorher __metadata("design:paramtypes", [prisma_service_1.PrismaService, ...]), nachher __metadata("design:paramtypes", [Function, Function, Function, Function, Function, Function]), und die require-Zeile des Dienstes verschwindet ersatzlos. Ueber den ganzen Workspace gemessen: 61 von 65 Dateien mit Abhaengigkeitsdaten werden beschaedigt. Die API startet danach nicht mehr. Dabei bleibt pnpm type-check gruen, und kein einziger der 1124 API-Tests faellt, denn grep -rl createTestingModule apps/api/src liefert 0 Treffer — der Testbestand startet den NestJS-Container nirgends. Der Fehler waere also durch jedes bestehende Tor dieses Projekts unbemerkt hindurchgegangen. Biome beschreibt das Problem in der eigenen Regelbeschreibung (biome explain useImportType, Abschnitt „Caveat with TypeScript experimental decorators") und empfiehlt dort woertlich, die Regel bei solchen Dekoratoren abzuschalten. Aufgabe 1 folgt dieser Empfehlung mit einem auf apps/api/** begrenzten Eintrag. Das ist eine Abweichung von D-02 und eine zweite Ausnahme neben D-01 — sie geschieht ausschliesslich, weil D-07 (kein Verhaltenswechsel) Vorrang hat, nicht um eine Zahl zu druecken.

  2. Nur 4 der 10 in D-02 genannten Regeln haben ueberhaupt eine Korrektur der Klasse „sicher". Probelauf je Regel mit --write ohne --unsafe: useImportType, noUselessEscapeInRegex, useConst und useExponentiationOperator aendern etwas; useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate, noUselessSwitchCase und useParseIntRadix melden „No fixes applied" und brauchen --unsafe. Deren Gesamtdiff ist mit 45 Dateien und rund 100 Zeilen klein genug, um ihn vollstaendig von Hand zu lesen — was Aufgabe 2 verlangt.

Purpose: Das Lint-Tor ist seit dem Vorgang 260921-9ie scharf. Ein Rueckstand von 2923 Warnungen macht es blind: eine neue, echte Warnung geht darin unter. Nach diesem Vorgang ist der Rueckstand klein genug, um ihn zu ueberblicken, und der Rest ist namentlich benannt statt anonym.

Output: Bereinigte biome.json, rund 100 bereinigte Quelldateien, aktualisierte Entwickleranleitung, ein ehrlicher Abschlussbericht.

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

@.planning/STATE.md @CLAUDE.md @biome.json @docs/anleitung-entwicklung.md

Messbefehl, der in allen drei Aufgaben gebraucht wird — einmal ausfuehren, Ergebnisdatei wiederverwenden:

LJ=$(mktemp /tmp/tessera-lint-XXXXXX.json)
pnpm exec biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null > "$LJ"
node -e '
const d=require(process.argv[1]).diagnostics;
const T=p=>/\.(spec|test)\.(ts|tsx)$/.test(p);
const r=d.filter(x=>!T(x.location.path));
console.log("total",d.length,"| real",r.length,"| test",d.length-r.length,
            "| errors",d.filter(x=>x.severity==="error").length);
const m={};for(const x of r)m[x.category]=(m[x.category]||0)+1;
console.log(Object.entries(m).sort((a,b)=>b[1]-a[1]).map(([k,v])=>String(v).padStart(4)+" "+k).join("\n"));
' "$LJ"

Wichtig zur Messung: Der Lauf muss aus dem Wurzelverzeichnis kommen. pnpm lint fuehrt biome lint . je Workspace aus und erreicht biome.json selbst nicht; die Grundmessung oben tut es. Ausserdem zwischenspeichert Turborepo den Lint-Schritt — fuer die Toraussage pnpm lint --force verwenden, sonst meldet der Lauf ein altes Ergebnis.

Aufgabe 1: Konfiguration bereinigen und die Messgrundlage herstellen biome.json, docs/anleitung-entwicklung.md Der Arbeitsbaum ist sauber (`git status --porcelain` liefert nichts). Die drei Durchgaenge bauen aufeinander auf und werden je einzeln festgeschrieben; ein vorbelasteter Baum macht die Diff-Nachweise unlesbar. `biome.json` an drei Stellen aendern, sonst nirgends.

(a) Ausnahme fuer Testdateien (D-01). Einen overrides-Block anlegen, erster Eintrag: includes auf die vier Muster **/*.spec.ts, **/*.spec.tsx, **/*.test.ts, **/*.test.tsx, darin linter.rules.suspicious.noExplicitAny auf off. Diese Muster sind gegen den Bestand geprueft: git ls-files findet 71 Dateien auf .spec.ts, 50 auf .test.tsx, 14 auf .test.ts und keine auf .spec.tsx; letzteres Muster bleibt der Symmetrie halber stehen. Weitere Testablagen gibt es nicht — keine __tests__- und keine __mocks__-Verzeichnisse im Bestand.

(b) Ausnahme fuer apps/api (Abweichung von D-02, begruendet in <objective> Punkt 1). Zweiter overrides-Eintrag: includes auf apps/api/**, darin linter.rules.style.useImportType auf off. In denselben Eintrag gehoert keine weitere Regel. Der Grund muss als Satz in der Entwickleranleitung stehen, nicht nur in der Festschreibung.

(c) Fehlerhafte Fixture-Ausnahme reparieren. files.includes enthaelt heute den Eintrag fuer das Fixture-Verzeichnis mit angehaengtem Doppelstern. Biome meldet das selbst als lint/suspicious/useBiomeIgnoreFolder in biome.json:11 und nennt die richtige Form in seinem Hinweis: seit Version 2.2.0 wird ein Verzeichnis ohne den angehaengten Doppelstern ausgenommen. Den Eintrag entsprechend kuerzen. Das betrifft die sechs Dateien unter apps/api/src/tenders/__fixtures__/.

Was NICHT geschieht (D-06): Der Block linter.rules wird an keiner anderen Stelle angefasst. Die Gruppe security kommt in der Datei weder vorher noch nachher vor und erbt damit weiter error aus preset: recommended. Die in 260921-9ie bewusst auf warn gesetzten Gruppen bleiben, wie sie sind.

Danach docs/anleitung-entwicklung.md im Abschnitt zum Lint-Tor (heute rund Zeile 61-69) fortschreiben. Der Satz „Aktuell stehen rund 2800 solcher Warnungen offen" ist ab jetzt falsch. Der neue Text nennt, in ganzen Saetzen und auf Deutsch: den Stand nach diesem Vorgang; dass any in Testdateien absichtlich nicht mehr gemeldet wird, weil es dort ausnahmslos an Attrappen haengt und eine Umschreibung viel Bewegung bei null Gewinn waere; dass useImportType in apps/api abgeschaltet ist, weil die Korrektur dort die Abhaengigkeitsaufloesung von NestJS zerstoert, Biome das in der eigenen Regelbeschreibung einraeumt und der Schaden von keinem Tor dieses Projekts bemerkt wuerde; und die Liste dessen, was als eigener Durchgang folgt (siehe Aufgabe 3, Abschnitt „Was bewusst stehen bleibt").

1. Konfiguration ist gueltiges JSON, hat genau zwei Ausnahmen, und security kommt nicht vor

node -e ' const c=require("/home/vicolab/projects/tessera-ctl/biome.json"); const ok=[]; ok.push(["overrides-Anzahl", (c.overrides||[]).length===2]); ok.push(["Testmuster", JSON.stringify(c.overrides[0].includes)===JSON.stringify(["/*.spec.ts","/.spec.tsx","**/.test.ts","/*.test.tsx"])]); ok.push(["Testregel", c.overrides[0].linter.rules.suspicious.noExplicitAny==="off"]); ok.push(["api-Pfad", JSON.stringify(c.overrides[1].includes)===JSON.stringify(["apps/api/"])]); ok.push(["api-Regel", c.overrides[1].linter.rules.style.useImportType==="off"]); ok.push(["security nirgends genannt", !JSON.stringify(c).includes("security")]); ok.push(["Fixture-Muster ohne Doppelstern", c.files.includes.includes("!**/fixtures")]); let bad=0; for(const [n,v] of ok){ if(!v) bad++; console.log((v?"OK ":"FEHL")+" "+n); } process.exit(bad);'

2. Zaehlung: Gesamt 754, echt 633, Test 121, Fehler 0

LJ=$(mktemp /tmp/tessera-lint-XXXXXX.json) pnpm exec biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null > "$LJ" node -e ' const d=require(process.argv[1]).diagnostics; const T=p=>/.(spec|test).(ts|tsx)$/.test(p); const r=d.filter(x=>!T(x.location.path)).length, t=d.length-r; const e=d.filter(x=>x.severity==="error").length; const any=d.filter(x=>x.category==="lint/suspicious/noExplicitAny"); const anyReal=any.filter(x=>!T(x.location.path)).length, anyTest=any.length-anyReal; console.log("gesamt",d.length,"echt",r,"test",t,"fehler",e,"| any echt",anyReal,"any test",anyTest); const exp=d.length,754],[r,633],[t,121],[e,0],[anyReal,289],[anyTest,0; process.exit(exp.filter(([a,b])=>a!==b).length);' "$LJ"

3. Das Tor bleibt gruen (Turbo-Zwischenspeicher umgehen)

pnpm lint --force pnpm type-check biome.json traegt genau zwei overrides-Eintraege und die korrigierte Fixture-Ausnahme; das Wort security kommt in der Datei nicht vor. Der Gesamtstand steht bei 754 Befunden (633 echt, 121 Test), Fehlerstufe 0. Entscheidend: noExplicitAny steht weiterhin bei exakt 289 in echtem Quelltext und 0 in Testdateien — die Ausnahme reicht nachweislich nicht in den Produktivcode hinein (D-01). pnpm lint --force meldet 5/5, pnpm type-check 4/4. Die Entwickleranleitung nennt den neuen Stand und begruendet beide Ausnahmen. Eine Konfigurationsdatei und ein Dokumentabschnitt; beides ist mit einer Festschreibung zurueckgenommen.

Aufgabe 2: Maschinelle Korrekturen und toter Code 53 Dateien insgesamt. Davon 45 aus dem maschinellen Durchgang in apps/api/src, apps/api/scripts, apps/web/src, packages und apps/desktop/src/setup.html — deren genauen Satz bestimmt das Werkzeug, nagelt Anzahl und Zeilenbilanz fest. Namentlich dazu die 11 Fundstellen toten Codes (D-03, drei davon stehen schon im maschinellen Satz): apps/api/scripts/rls-scratch-check.mjs, apps/api/src/auth/decorators/current-user.decorator.ts, apps/api/src/auth/interceptors/force-password-change.interceptor.ts, apps/api/src/calendar/calendar.service.ts, apps/api/src/calendar/dto/create-calendar-source.dto.ts, apps/api/src/cert-manager/cert-manager.service.ts, apps/api/src/dkv/dkv-parser.service.ts, apps/web/src/app/(auth)/login/page.tsx, apps/web/src/app/(portal)/change-password/page.tsx, apps/web/src/app/(portal)/modules/cert-manager/components/SplitTab.tsx, apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx Aufgabe 1 ist festgeschrieben und der Arbeitsbaum ist wieder sauber. Ohne die Ausnahme fuer `apps/api` aus Aufgabe 1 wuerde der erste Befehl dieses Durchgangs 61 von 65 Dateien mit Abhaengigkeitsdaten beschaedigen. **Erst die beiden Nachweis-Anker setzen, dann anfassen.**

Anker 1 — der Ausgangspunkt im Verlauf. <verify> misst den Umfang gegen diesen Punkt und nicht gegen HEAD; sonst faellt die Messung leer aus, sobald die Arbeit festgeschrieben ist, und der Umfangsnachweis geht ins Leere:

BASE=$(git rev-parse HEAD)   # Stand nach Aufgabe 1; Wert notieren, in <verify> wiederverwenden

Anker 2 — der Abdruck der erzeugten Dekoratordaten. Er ist der eigentliche Beleg dafuer, dass sich nichts am Verhalten geaendert hat, und ein gruener Testlauf ersetzt ihn nicht:

SNAP=$(mktemp -d); pnpm --filter @tessera/api exec tsc --outDir "$SNAP" >/dev/null 2>&1
grep -rh '__metadata(' "$SNAP" | sort | sha256sum
# erwartet: 6e1583f1eb72a089eb0ed98f81158b54a9fbd40dbf41371292725f36ef764300  (593 Zeilen)

(A) Durchgang mit gesicherten Korrekturen. Vier Regeln haben eine Korrektur der Klasse „sicher"; sie duerfen ohne --unsafe laufen:

pnpm exec biome lint apps/web packages --only=lint/style/useImportType --write
for R in complexity/noUselessEscapeInRegex style/useConst style/useExponentiationOperator; do
  pnpm exec biome lint . --only="lint/$R" --write --max-diagnostics=20000
done

Die erste Zeile ist pfadgebunden, und das ist kein Schoenheitsfehler: --only=<regel> schaltet eine Regel, die in der Konfiguration auf off steht, fuer diesen Lauf wieder AN. Ein repo-weites biome lint . --only=lint/style/useImportType --write haengt die Ausnahme aus Aufgabe 1 also wirkungslos aus und schreibt 201 import type-Zeilen nach apps/api — beim Planen genau so passiert und gemessen. Der Pfad apps/web packages ist die einzige zulaessige Form. Betroffen sind dort 2 Fundstellen.

(B) Durchgang mit ungesicherten Korrekturen, danach vollstaendig von Hand gelesen. Sechs Regeln haben keine gesicherte Korrektur. Fuenf davon laufen mit --unsafe:

for R in style/useNodejsImportProtocol complexity/useLiteralKeys complexity/useOptionalChain \
         style/useTemplate correctness/useParseIntRadix; do
  pnpm exec biome lint . --only="lint/$R" --write --unsafe --max-diagnostics=20000
done

Danach git diff vollstaendig lesen — es sind rund 100 Zeilen, das ist zumutbar und verlangt (D-02: nie ungeprueft anwenden). Zwei Stellen brauchen dabei ausdruecklich Aufmerksamkeit, weil sie in Wegen liegen, die dieses Projekt schon einmal Zeit gekostet haben:

  • apps/api/src/ldap/ldap.service.ts traegt 25 der 31 useLiteralKeys-Aenderungen. Es geht um Verzeichnis-Merkmale, die heute in eckigen Klammern gelesen werden und danach mit Punkt. Das ist in JavaScript dieselbe Operation; die Schreibweise der Merkmalsnamen (objectGUID, sAMAccountName, cn, ou, mail) darf sich dabei an keiner Stelle aendern — ein verschluckter Grossbuchstabe macht den AD-Abgleich still leer. Zeichenweise gegenlesen.
  • apps/api/src/auth/strategies/jwt.strategy.ts und apps/api/src/auth/auth.service.ts liegen im Anmeldeweg. Dort entstehen Verkuerzungen wie „kein Benutzer oder Benutzer nicht aktiv" zu einer einzigen abgesicherten Kette. Pruefen, dass jede dieser Verkuerzungen dieselbe Entscheidung faellt wie vorher — insbesondere, dass eine fehlende Sitzung weiterhin zur Abweisung fuehrt und nicht zum Durchwinken.

Die sechste Regel wird bewusst NICHT angewendet. complexity/noUselessSwitchCase meldet eine Fundstelle in apps/api/src/tenders/tender-normalizer.service.ts:60. Die Regel moechte dort eine Fallmarke streichen, die unmittelbar ueber einem Kommentar steht, der erklaert, warum der Standardzweig genau auf diesem Weg bleiben muss. Die Marke dokumentiert also Absicht, die der Regel entgeht. Sie bleibt stehen, wird nicht unterdrueckt und im Abschlussbericht als bewusster Nicht-Fix genannt — das ist ehrlicher als eine Unterdrueckung, die wie eine Erledigung aussieht.

(C) Toter Code (D-03), 15 Fundstellen. Jede einzeln lesen, bevor etwas verschwindet. Drei Gruppen:

  1. Echt tot, folgenlos entfernbar — die nicht benutzten Fehlervariablen in calendar.service.ts (Zeilen 321, 358), cert-manager.service.ts (305, 679) und dkv-parser.service.ts (42): den Namen aus der Auffangklausel streichen, den Rumpf und damit den Kontrollfluss unveraendert lassen. Dazu die nicht benutzten Einfuhren in create-calendar-source.dto.ts:2 und die nicht benutzte Funktion forSystemQuery in rls-scratch-check.mjs:226 (ein Pruefskript, nicht im Auslieferungsweg).
  2. Nicht entfernbar, nur umbenennen — current-user.decorator.ts:4: der Wert data ist der erste von zwei Parametern, die NestJS positionsgebunden uebergibt. Streichen wuerde den zweiten verschieben. Stattdessen mit fuehrendem Unterstrich kennzeichnen.
  3. Symptome, die gemeldet und NICHT stillschweigend repariert werden — hier ist die unbenutzte Variable der Hinweis auf eine echte Luecke, und sie zu schliessen waere ein Verhaltenswechsel, den D-07 diesem Vorgang verbietet:
    • force-password-change.interceptor.ts:53 liest das HTTP-Verfahren in eine Variable und befragt sie nie; die Freigabeliste unterscheidet also nicht zwischen Lese- und Schreibzugriff auf die freigegebenen Wege. Variable entfernen, Luecke im Bericht als Folgeaufgabe nennen.
    • change-password/page.tsx:11-12 haelt Wegweiser und Benutzerablage vor und benutzt beide nicht. Beim Lesen der Datei bestaetigt: nach erfolgreichem Wechsel wird weder weitergeleitet noch die Benutzerablage aufgefrischt — bei erzwungenem Wechsel bleibt die Person auf der Seite stehen. Die drei Bindungen entfernen, den Befund im Bericht als Folgeaufgabe nennen.
    • VehicleTable.tsx:164 setzt einen Laufzustand fuers Loeschen, liest ihn aber nie; die Loeschschaltflaeche hat also keinen Besetztzustand und laesst sich doppelt ausloesen. Nur die lesende Bindung entfernen, die setzende bleibt; Befund im Bericht als Folgeaufgabe nennen.
    • SplitTab.tsx:20 bekommt die Uebersetzungsfunktion und benutzt sie nicht — ein Hinweis auf fest verdrahtete Texte in diesem Reiter. Parameter entfernen, Befund melden.
    • login/page.tsx:21 haelt einen unbenutzten Wegweiser; hier ist die Weiterleitung nachweislich anderswo geloest. Entfernen, keine Meldung noetig.

Kein biome format --write, auch nicht auf einzelne Dateien (D-08). Der Diff muss zeilenbilanziert bleiben.

1. STAERKSTER NACHWEIS: die Abhaengigkeitsdaten von NestJS sind Zeichen fuer Zeichen unveraendert.

Ein gruener Testlauf kann das NICHT belegen — kein Test dieses Projekts startet den Container

(grep -rl createTestingModule apps/api/src liefert 0 Treffer).

SNAP=$(mktemp -d); pnpm --filter @tessera/api exec tsc --outDir "$SNAP" >/dev/null 2>&1 test "$(grep -rh '__metadata(' "$SNAP" | wc -l)" = "593" || { echo "FEHL: Zeilenzahl der Dekoratordaten"; exit 1; } grep -rh '__metadata(' "$SNAP" | sort | sha256sum
| grep -q '^6e1583f1eb72a089eb0ed98f81158b54a9fbd40dbf41371292725f36ef764300'
|| { echo "FEHL: Dekoratordaten veraendert — Abhaengigkeitsaufloesung gefaehrdet"; exit 1; } echo "OK Dekoratordaten unveraendert (593 Zeilen)"

2. Umfang festgenagelt: 53 Quelldateien (45 aus dem maschinellen Durchgang + 11 toter Code,

davon 3 in beiden), und der Diff hat keinen Zeilenueberschuss (keine Formatierung, D-08).

$BASE ist der in notierte Stand nach Aufgabe 1 — gegen HEAD gemessen waere die

Pruefung leer und damit wertlos, sobald die Arbeit festgeschrieben ist.

git-Ausgabe zuerst einfangen, damit ein Fehlschlag von git nicht in einer Pipe verschwindet.

test -n "$BASE" || { echo "FEHL: BASE nicht gesetzt (siehe , Anker 1)"; exit 1; } CHANGED=$(git diff --name-only "$BASE") || { echo "FEHL: git diff --name-only"; exit 1; } N=$(printf '%s\n' "$CHANGED" | grep -v '^(biome.json|docs/)' | grep -c .) test "$N" = "53" || { echo "FEHL: $N statt 53 Quelldateien"; printf '%s\n' "$CHANGED"; exit 1; } STAT=$(git diff --numstat "$BASE" -- ':!biome.json' ':!docs') || { echo "FEHL: git diff --numstat"; exit 1; } printf '%s\n' "$STAT" | awk ' {a+=$1;b+=$2} END {print "Zugaenge",a,"Abgaenge",b; if (a>b) {print "FEHL: Zeilenueberschuss — Formatierung mitgelaufen?"; exit 1}}'

Zwischenprobe, direkt nach Schritt (B) und VOR dem toten Code auszufuehren:

MID=$(git diff --name-only "$BASE") ; printf '%s\n' "$MID" | grep -vc '^biome.json'

-> muss 45 ergeben

3. Die neun bearbeiteten Regeln stehen auf 0, die bewusst stehen gelassene auf 1

LJ=$(mktemp /tmp/tessera-lint-XXXXXX.json) pnpm exec biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null > "$LJ" node -e ' const d=require(process.argv[1]).diagnostics; const c=r=>d.filter(x=>x.category==="lint/"+r).length; const soll={"style/useImportType":0,"style/useNodejsImportProtocol":0,"complexity/useLiteralKeys":0, "complexity/useOptionalChain":0,"style/useTemplate":0,"complexity/noUselessEscapeInRegex":0, "style/useConst":0,"style/useExponentiationOperator":0,"correctness/useParseIntRadix":0, "correctness/noUnusedVariables":0,"correctness/noUnusedImports":0, "correctness/noUnusedFunctionParameters":0,"complexity/noUselessSwitchCase":1}; let bad=0; for(const [r,s] of Object.entries(soll)){const v=c(r); if(v!==s){bad++;console.log("FEHL",r,v,"statt",s);} } const T=p=>/.(spec|test).(ts|tsx)$/.test(p); const re=d.filter(x=>!T(x.location.path)).length; console.log("gesamt",d.length,"echt",re,"test",d.length-re,"fehler",d.filter(x=>x.severity==="error").length); if(d.length!==621||re!==542) {bad++;console.log("FEHL Gesamtstand, erwartet 621/542");} if(d.filter(x=>x.severity==="error").length!==0){bad++;console.log("FEHL Fehlerstufe nicht 0");} process.exit(bad);' "$LJ"

4. Unveraenderte gruene Balken

pnpm --filter @tessera/api run test 2>&1 | grep -q "Test Files 69 passed (69)" || { echo "FEHL api Dateien"; exit 1; } pnpm --filter @tessera/api run test 2>&1 | grep -q "Tests 1124 passed (1124)" || { echo "FEHL api Tests"; exit 1; } pnpm --filter @tessera/web run test 2>&1 | grep -q "Test Files 66 passed (66)" || { echo "FEHL web Dateien"; exit 1; } pnpm --filter @tessera/web run test 2>&1 | grep -q "Tests 459 passed (459)" || { echo "FEHL web Tests"; exit 1; } pnpm type-check pnpm lint --force Die erzeugten Dekoratordaten aus apps/api sind unveraendert (593 Zeilen, sha256 6e1583f1...) — der maschinelle Durchgang hat die Abhaengigkeitsaufloesung von NestJS nachweislich nicht angetastet. Genau 45 Quelldateien sind geaendert, der Quelltext-Diff ist zeilenbilanziert (keine Formatierung mitgelaufen). Die neun bearbeiteten Regeln stehen auf 0, noUselessSwitchCase bewusst auf 1. Der Gesamtstand liegt bei 621 Befunden (542 echt, 79 Test), Fehlerstufe 0. Beide Testlaeufe sind punktgleich gruen (69/1124 und 66/459), pnpm type-check 4/4, pnpm lint --force 5/5. Die fuenf Symptomfunde aus D-03 sind im Bericht namentlich als Folgeaufgaben festgehalten, nicht stillschweigend repariert. 45 Dateien maschinell veraendert. Zuruecknehmen heisst eine Festschreibung verwerfen — technisch einfach, aber der von Hand gelesene Diff waere noch einmal zu lesen. Deshalb steht der Abdruck der Dekoratordaten VOR der ersten Aenderung.

Aufgabe 3: Barrierefreiheit von Hand 53 Dateien, ueberwiegend apps/web/src/components und apps/web/src/app, dazu apps/web/src/app/icon.svg und apps/desktop/src/setup.html. Schwerpunkte (Befunde je Datei): apps/web/src/app/(portal)/admin/ldap/page.tsx (15), apps/web/src/app/(portal)/admin/users/page.tsx (11), apps/web/src/components/settings/calendar-settings-panel.tsx (11), apps/web/src/components/layout/sidebar.tsx (9), apps/web/src/app/(portal)/admin/tenants/page.tsx (8), apps/web/src/components/dashboard/widget-catalog-modal.tsx (8), apps/web/src/components/layout/header.tsx (8), apps/web/src/components/admin/admin-sidebar.tsx (7), apps/web/src/components/dashboard/widget-registry.tsx (7) Aufgabe 2 ist festgeschrieben und der Arbeitsbaum ist sauber. Die Zaehlungen in diesem Durchgang gehen vom Stand 621/542/79 aus. Zuerst den Ausgangspunkt im Verlauf festhalten, damit `` den Umfang messen kann, auch wenn die Arbeit zwischendurch festgeschrieben wird:
BASE3=$(git rev-parse HEAD)   # Stand nach Aufgabe 2

Hier hilft kein Werkzeug: fuer alle sechs Regeln dieses Durchgangs liefert Biome weder eine gesicherte noch eine ungesicherte Korrektur — beim Planen je Regel mit --write und mit --write --unsafe geprueft, Ergebnis jeweils null geaenderte Dateien (einzige Ausnahme noRedundantRoles mit 3 Dateien unter --unsafe). 155 Fundstellen in 53 Dateien, von Hand.

Bearbeitet werden diese sechs Regeln:

Regel Befunde Vorgehen
a11y/noSvgWithoutTitle 71 je Symbol entscheiden
a11y/useButtonType 52 je Schaltflaeche entscheiden
a11y/noLabelWithoutControl 22 Beschriftung an Feld binden
a11y/useAriaPropsSupportedByRole 5 Merkmal oder Rolle richtigstellen
a11y/noRedundantRoles 4 doppelte Rollenangabe entfernen
a11y/useAriaPropsForRole 1 fehlendes Pflichtmerkmal ergaenzen

Symbole (71). Alle Symbole in diesem Projekt sind eingebettete Grafiken; eine Symbolbibliothek gibt es nicht. Je Fundstelle eine von zwei Entscheidungen, und die Entscheidung haengt daran, ob daneben schon Text steht:

  • Das Symbol begleitet sichtbaren Text (Menueeintrag mit Beschriftung, Schaltflaeche mit Wort) → es ist Schmuck und wird fuer die Vorlesehilfe ausgeblendet. Sonst liest sie die Sache doppelt.
  • Das Symbol steht allein und traegt die ganze Bedeutung (Schliessen-Kreuz, Zahnrad, Lupe ohne Wort) → es bekommt einen Titel, der sagt, was die Bedienung tut, nicht wie sie aussieht. Der Text gehoert in den Uebersetzungskatalog, wenn die umgebende Datei bereits uebersetzt; sonst als deutscher Klartext, passend zum Umfeld. Zwei Fundstellen liegen ausserhalb der React-Oberflaeche: apps/web/src/app/icon.svg (die Bildmarke — Titel mit dem Produktnamen) und apps/desktop/src/setup.html:173 (Einrichtungsseite des Desktop-Programms).

Schaltflaechen (52). Hier ist die Regel kein Formfehler, sondern deckt echte Fehlbedienung auf: eine Schaltflaeche ohne Typangabe innerhalb eines Formulars sendet das Formular ab. Deshalb je Fundstelle lesen, was die Schaltflaeche tatsaechlich tun soll, und entsprechend auszeichnen — nicht pauschal dasselbe eintragen. Nur drei der 25 betroffenen Dateien enthalten ueberhaupt ein Formular:

  • admin/tenants/page.tsx — 1 Formular, 6 Fundstellen
  • admin/users/page.tsx — 1 Formular, 6 Fundstellen
  • admin/ldap/page.tsx — 2 Formulare, 4 Fundstellen In diesen drei Dateien ist der Absendeknopf jeweils schon richtig ausgezeichnet (gezaehlt: 1 / 1 / 2). Die 16 Fundstellen dort sind also durchweg Neben-Schaltflaechen — Abbrechen, Schliessen, Zeilenaktionen —, die heute beim Klick ungewollt absenden. Sie bekommen die Auszeichnung fuer „tut etwas anderes". Damit aendert sich Verhalten, und zwar genau das kaputte: das ist der Zweck der Regel und ausdruecklich von D-04 gedeckt. Die Zahl der absendenden Schaltflaechen je Datei muss danach unveraendert bei 1 / 1 / 2 stehen — das prueft <verify>. In den uebrigen 22 Dateien gibt es kein Formular; dort ist die Auszeichnung reine Absicherung.

Beschriftungen (22). Beschriftung und Eingabefeld ueber eine Kennung verbinden, oder die Beschriftung um das Feld legen. Kennungen muessen je Seite eindeutig bleiben — in Listen mit wiederholten Zeilen die Zeilenkennung in die Feldkennung aufnehmen. Danach gehoert zu jeder Beschriftung genau ein Feld.

Rollen und Merkmale (10). Die fuenf Merkmale, die zur gesetzten Rolle nicht passen, das eine fehlende Pflichtmerkmal und die vier doppelt gesetzten Rollen einzeln richtigstellen. Bei noRedundantRoles darf der maschinelle Vorschlag als Ausgangspunkt dienen (pnpm exec biome lint apps/web --only=lint/a11y/noRedundantRoles --write --unsafe, 3 Dateien), das Ergebnis ist trotzdem zu lesen.

Was bewusst stehen bleibt (30 Befunde, D-05). Diese sechs Regeln werden NICHT bearbeitet, weil jede eine Gestaltungsentscheidung oder einen Verhaltenswechsel verlangt, den dieser Vorgang nicht treffen darf:

  • noNoninteractiveElementInteractions (11) und noStaticElementInteractions (5) — verlangen die Entscheidung, ob aus einem geklickten Bereich eine echte Bedienung wird oder der Klick weg soll.
  • useKeyWithClickEvents (5) — verlangt einen Tastaturweg, den es heute nicht gibt; das ist neue Bedienung, kein Aufraeumen.
  • noAutofocus (4) — das Entfernen verschiebt den Eingabefokus beim Seitenaufruf und ist damit ein Verhaltenswechsel, den D-07 hier verbietet.
  • useSemanticElements (4) — verlangt einen Austausch von Bauelementen mitsamt Gestaltung.
  • noNoninteractiveTabindex (1) — aendert die Tabulatorreihenfolge. Diese 30 gehoeren in den Bericht und in die Entwickleranleitung, nicht in eine stille Ablage. Keine dieser Regeln wird herabgestuft oder abgeschaltet (D-06).

Abschlussbericht. Zum Schluss den Vorher/Nachher-Stand festhalten: 2923 → 466, getrennt nach echtem Quelltext (856 → 387) und Testdateien (2067 → 79), mit der Aufschluesselung dessen, was bleibt, und den in Aufgabe 2 gefundenen Symptomen als benannte Folgeaufgaben. Denselben Stand in docs/anleitung-entwicklung.md nachziehen, falls die in Aufgabe 1 geschriebene Zahl abweicht.

1. Die sechs bearbeiteten Regeln stehen auf 0, die sechs zurueckgestellten unveraendert

LJ=$(mktemp /tmp/tessera-lint-XXXXXX.json) pnpm exec biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null > "$LJ" node -e ' const d=require(process.argv[1]).diagnostics; const c=r=>d.filter(x=>x.category==="lint/a11y/"+r).length; const erledigt={noSvgWithoutTitle:0,useButtonType:0,noLabelWithoutControl:0, useAriaPropsSupportedByRole:0,noRedundantRoles:0,useAriaPropsForRole:0}; const zurueck={noNoninteractiveElementInteractions:11,noStaticElementInteractions:5, useKeyWithClickEvents:5,noAutofocus:4,useSemanticElements:4,noNoninteractiveTabindex:1}; let bad=0; for(const [r,s] of Object.entries(erledigt)) if(c(r)!==s){bad++;console.log("FEHL erledigt",r,c(r),"statt",s);} for(const [r,s] of Object.entries(zurueck)) if(c(r)!==s){bad++;console.log("FEHL zurueckgestellt",r,c(r),"statt",s);} const T=p=>/.(spec|test).(ts|tsx)$/.test(p); const re=d.filter(x=>!T(x.location.path)).length; console.log("ENDSTAND gesamt",d.length,"echt",re,"test",d.length-re, "fehler",d.filter(x=>x.severity==="error").length); if(d.length!==466||re!==387){bad++;console.log("FEHL Endstand, erwartet 466/387");} if(d.filter(x=>x.severity==="error").length!==0){bad++;console.log("FEHL Fehlerstufe nicht 0");} process.exit(bad);' "$LJ"

2. Keine a11y-Regel wurde herabgestuft, um diese Zahlen zu erreichen (D-06)

node -e ' const c=require("/home/vicolab/projects/tessera-ctl/biome.json"); const s=JSON.stringify(c.linter.rules.a11y)+JSON.stringify(c.overrides); const ok = c.linter.rules.a11y==="warn" && !s.includes("a11y/") && !/"a11y"\s*:\s*{/.test(JSON.stringify(c.overrides)); console.log(ok?"OK a11y unveraendert auf warn, keine Einzelausnahme":"FEHL a11y angefasst"); process.exit(ok?0:1);'

3. Kein Formular hat seinen Absendeknopf verloren, keine Nebenschaltflaeche sendet mehr ab

cd /home/vicolab/projects/tessera-ctl for f in "apps/web/src/app/(portal)/admin/tenants/page.tsx:1"
"apps/web/src/app/(portal)/admin/users/page.tsx:1"
"apps/web/src/app/(portal)/admin/ldap/page.tsx:2"; do p="${f%:}"; soll="${f##:}" ist=$(grep -c 'type="submit"' "$p") formulare=$(grep -c '<form' "$p") test "$ist" = "$soll" && test "$formulare" = "$soll"
|| { echo "FEHL $p: $ist absendende Schaltflaechen bei $formulare Formularen, erwartet $soll"; exit 1; } echo "OK $p $ist/$formulare" done

4. Der API-Teil ist in diesem Durchgang gar nicht angefasst worden.

$BASE3 ist der in notierte Stand nach Aufgabe 2; gegen HEAD gemessen liefe die

Pruefung nach dem Festschreiben leer. git-Ausgabe zuerst einfangen — ein Fehlschlag von git

saehe sonst aus wie "nichts geaendert".

test -n "$BASE3" || { echo "FEHL: BASE3 nicht gesetzt (siehe )"; exit 1; } API_DIFF=$(git diff --name-only "$BASE3" -- apps/api packages) || { echo "FEHL: git diff"; exit 1; } test -z "$API_DIFF" || { echo "FEHL: apps/api oder packages veraendert:"; printf '%s\n' "$API_DIFF"; exit 1; }

5. Unveraenderte gruene Balken

pnpm --filter @tessera/web run test 2>&1 | grep -q "Test Files 66 passed (66)" || { echo "FEHL web Dateien"; exit 1; } pnpm --filter @tessera/web run test 2>&1 | grep -q "Tests 459 passed (459)" || { echo "FEHL web Tests"; exit 1; } pnpm --filter @tessera/api run test 2>&1 | grep -q "Tests 1124 passed (1124)" || { echo "FEHL api Tests"; exit 1; } pnpm type-check pnpm lint --force Die 52 Schaltflaechen-Aenderungen sind die einzige Stelle dieses Vorgangs, an der sich absichtlich Verhalten aendert, und ein gruener Testlauf deckt davon nur einen Teil ab. Darum im Entwicklungsbetrieb (pnpm dev, nicht gegen den Testserver) die drei Formularseiten der Verwaltung oeffnen — Mandanten, Benutzer, LDAP — und je Seite zwei Dinge pruefen: das Formular laesst sich weiterhin ueber seinen Absendeknopf abschicken, und ein Klick auf Abbrechen beziehungsweise Schliessen schickt es NICHT ab. Nicht ueber Netzwerkaufrufe aus der Seite heraus messen, sondern die Oberflaeche bedienen. Die sechs bearbeiteten Barrierefreiheits-Regeln stehen auf 0 (155 Fundstellen in 53 Dateien von Hand erledigt), die sechs zurueckgestellten stehen unveraendert bei zusammen 30 und sind im Bericht namentlich mit Begruendung aufgefuehrt. Keine a11y-Regel wurde dafuer herabgestuft oder in eine Ausnahme gelegt. In den drei Formularseiten steht die Zahl der absendenden Schaltflaechen unveraendert bei 1 / 1 / 2, und die Bedienprobe bestaetigt, dass Absenden weiter geht und Abbrechen nicht mehr absendet. apps/api und packages sind in diesem Durchgang unberuehrt. Endstand: 466 Befunde (387 echt, 79 Test), Fehlerstufe 0 — von 2923 zu Beginn. Beide Testlaeufe punktgleich gruen, pnpm type-check 4/4, pnpm lint --force 5/5. Handarbeit an der Oberflaeche, ausschliesslich apps/web und apps/desktop; jede Datei einzeln zuruecknehmbar.

<threat_model>

Trust Boundaries

Boundary Description
Lint-Konfiguration → CI-Tor biome.json entscheidet, was der CI-Schritt „Lint" ueberhaupt sehen kann. Eine zu weit gefasste Ausnahme blendet echte Befunde dauerhaft aus, ohne dass jemand es bemerkt.
Werkzeug → Quelltext biome lint --write aendert Dateien ohne menschlichen Zwischenschritt, auch in Anmelde-, Sitzungs- und Verzeichnisabgleich-Wegen.
Erzeugtes JS → NestJS-Laufzeit Die Abhaengigkeitsaufloesung liest Daten, die der Compiler aus Typangaben erzeugt. Kein Tor dieses Projekts prueft diese Daten heute.
Browser → Formular Eine Schaltflaeche ohne Typangabe loest im Formular ein Absenden aus; ihre Auszeichnung entscheidet, ob ein Klick Daten schreibt.

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-bi2-01 Tampering biome.json overrides[1] (apps/api/**, useImportType off) high mitigate Nur moeglich, weil die Alternative beweisbar schlimmer ist: 61 von 65 Dateien mit Abhaengigkeitsdaten werden vom maschinellen Fix zerstoert, bei gruenem tsc und gruenen 1124 Tests. Begrenzt auf genau eine Regel und einen Pfad; <verify> in Aufgabe 1 prueft die Form des Eintrags und dass er keine zweite Regel enthaelt. Begruendung steht schriftlich in der Entwickleranleitung, nicht nur in der Festschreibung.
T-bi2-02 Tampering NestJS-Abhaengigkeitsdaten, gesamter maschineller Durchgang high mitigate sha256 ueber alle 593 erzeugten __metadata-Zeilen vor und nach dem Durchgang; muss identisch bleiben (6e1583f1...). Beim Planen mit dem vollstaendigen Durchgang erprobt: Abdruck blieb identisch. Das ist der einzige Nachweis, der hier traegt — grep -rl createTestingModule apps/api/src liefert 0 Treffer, der Testbestand startet den Container nirgends.
T-bi2-03 Information Disclosure biome.json overrides[0] (Testdateien) medium mitigate Die Ausnahme koennte echten Produktivcode mit abdecken. Geprueft und ausgeschlossen: keine Datei auf .spec/.test exportiert ein Symbol (grep -rln "^export" --include=*.spec.ts --include=*.test.ts --include=*.test.tsx liefert nichts), und keine Datei ausserhalb dieser Muster fuehrt eine solche Datei ein. Laufender Nachweis: noExplicitAny bleibt bei exakt 289 in echtem Quelltext — waere die Ausnahme zu weit, faellt diese Zahl. Sie deckt genau eine Regel ab, nicht die Gruppe.
T-bi2-04 Tampering apps/api/src/ldap/ldap.service.ts (25 useLiteralKeys-Aenderungen) high mitigate Der AD-Abgleich liest Verzeichnis-Merkmale ueber ihren Namen; eine verschluckte Grossschreibung macht ihn still leer, und AD ist in diesem Projekt bewusst nur lesend angebunden, also faellt es erst beim Anmelden auf. Diff zeichenweise gegenlesen (in <action> verlangt), zusaetzlich ldap.service.spec.ts im 1124er-Lauf und tsc ueber die getypten Zugriffe.
T-bi2-05 Elevation of Privilege jwt.strategy.ts, auth.service.ts (useOptionalChain, useLiteralKeys) high mitigate Die Verkuerzungen liegen in Sitzungs-Cookie-Lesung und Aktiv-Pruefung des Kontos. Falsch verkuerzt liesse eine fehlende Sitzung durch. Der Gesamtdiff ist rund 100 Zeilen und wird vollstaendig gelesen; diese beiden Dateien sind in <action> namentlich als Lesepflicht markiert. auth.service.spec.ts und jwt.strategy.spec.ts laufen im 1124er-Bestand mit.
T-bi2-06 Tampering Entfernen „unbenutzter" Werte (D-03, 15 Stellen) medium mitigate Ein Wert kann eine Nebenwirkung tragen. Darum drei Gruppen mit unterschiedlicher Behandlung statt eines pauschalen Loeschens: folgenlose Auffangvariablen werden entfernt, der positionsgebundene Dekoratorparameter wird nur umbenannt statt gestrichen, und fuenf Fundstellen sind Symptome echter Luecken und werden gemeldet statt repariert. <verify> nagelt den Diff auf 45 Dateien und Zeilenbilanz fest.
T-bi2-07 Tampering 52 Schaltflaechen in apps/web (useButtonType) medium mitigate Eine falsch ausgezeichnete Schaltflaeche kann ein Formular entweder unabsendbar machen oder weiter ungewollt absenden lassen. Nur 3 der 25 Dateien enthalten ueberhaupt ein Formular; dort wird die Zahl der absendenden Schaltflaechen auf 1 / 1 / 2 festgenagelt, und eine Bedienprobe prueft beide Richtungen (Absenden geht, Abbrechen sendet nicht).
T-bi2-08 Repudiation Regelgruppe security, gesamte Konfiguration high mitigate Der naheliegende Missbrauch dieses Vorgangs waere, Zahlen ueber Herabstufungen zu druecken. Dagegen drei Pruefungen: die Zeichenkette security darf in biome.json ueberhaupt nicht vorkommen (erbt damit error aus dem Vorgabesatz), linter.rules.a11y muss unveraendert auf warn stehen, und in overrides darf keine a11y-Regel auftauchen. Genau zwei Ausnahmen sind zugelassen, beide namentlich in <verify> von Aufgabe 1 beschrieben.
T-bi2-09 Denial of Service CI-Schritt „Lint" medium mitigate Ein neuer Befund der Stufe Fehler faerbt CI rot. Jede der drei Aufgaben prueft errors === 0 in der JSON-Zaehlung und laesst zusaetzlich pnpm lint --force laufen — mit --force, weil Turborepo den Schritt sonst aus dem Zwischenspeicher als gruen meldet.
</threat_model>
Nach der letzten Aufgabe, aus dem Wurzelverzeichnis:
LJ=$(mktemp /tmp/tessera-lint-XXXXXX.json)
pnpm exec biome lint . --reporter=json --max-diagnostics=20000 2>/dev/null > "$LJ"
node -e '
const d=require(process.argv[1]).diagnostics;
const T=p=>/\.(spec|test)\.(ts|tsx)$/.test(p);
const r=d.filter(x=>!T(x.location.path)).length;
console.log("gesamt",d.length,"(Start 2923) | echt",r,"(Start 856) | test",d.length-r,"(Start 2067)");
console.log("fehler",d.filter(x=>x.severity==="error").length,"(muss 0 sein)");
' "$LJ"

SNAP=$(mktemp -d); pnpm --filter @tessera/api exec tsc --outDir "$SNAP" >/dev/null 2>&1
grep -rh '__metadata(' "$SNAP" | sort | sha256sum   # 6e1583f1eb72a089eb0ed98f81158b54a9fbd40dbf41371292725f36ef764300

pnpm type-check          # 4/4
pnpm lint --force        # 5/5, 0 Fehler
pnpm --filter @tessera/api run test   # 69 Dateien / 1124 Tests
pnpm --filter @tessera/web run test   # 66 Dateien / 459 Tests

Zur Ehrlichkeit des Nachweises: Ein gruener Testbestand allein belegt fuer einen maschinellen Durchgang ueber 45 Dateien nicht, dass sich nichts geaendert hat — dieser Plan hat beim Planen einen Fall gemessen, der genau das zeigt (import type in apps/api laesst tsc und alle 1124 Tests gruen und zerstoert trotzdem den Start der Anwendung). Die tragenden Nachweise sind deshalb, in dieser Reihenfolge: der unveraenderte Abdruck der Dekoratordaten, der zeilenbilanzierte und vollstaendig von Hand gelesene Diff, die regelweisen Zaehlungen, und erst danach die gruenen Testlaeufe.

<success_criteria>

  • Lint-Rueckstand von 2923 auf 466 gesenkt (echt 856 → 387, Test 2067 → 79), Fehlerstufe durchgehend 0.
  • biome.json traegt genau zwei zielgenaue Ausnahmen; security kommt in der Datei nicht vor; keine a11y-Regel herabgestuft.
  • noExplicitAny steht nach der Testdatei-Ausnahme unveraendert bei 289 in echtem Quelltext.
  • Die erzeugten NestJS-Dekoratordaten sind Zeichen fuer Zeichen unveraendert (593 Zeilen, sha256 6e1583f1...).
  • pnpm type-check 4/4, pnpm lint --force 5/5, apps/api 69/1124, apps/web 66/459 — alle punktgleich zum Ausgangsstand.
  • Quelltext-Diff zeilenbilanziert; keine repo-weite Formatierung angestossen.
  • Bericht nennt Vorher/Nachher getrennt nach Testdateien und echtem Quelltext, die 30 zurueckgestellten a11y-Befunde mit Begruendung, die fuenf Symptomfunde aus D-03 und die Regeln, die als eigener Durchgang folgen.
  • Entwickleranleitung gibt den neuen Stand und beide Ausnahmen wieder. </success_criteria>
Create `.planning/quick/260921-bi2-lint-rueckstand-abbauen-mechanische-fixe/260921-bi2-SUMMARY.md` when done