Files
tickte-system/docs/superpowers/plans/2026-10-05-weisses-design-p10-aufraeumen.md
T
ClaudeandClaude Opus 5.5 4af78f0bad Plaene fuer das weisse Design (Uebersicht, P0-P10)
Umsetzungsplaene je Paket mit gemeinsamen Regeln (env.sh/check.sh,
Gate-Schema, CSS-Reihenfolge, TP2-Pruefung vor jedem Paket) und den
Schnittstellen aus P1. Gegen Spec und untereinander geprueft (zwei
Pruefer, Korrekturrunde, Nachpruefung).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 06:02:54 +00:00

680 lines
51 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WOMS „Weißes Design“ P10: Aufräumen – Umsetzungsplan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Nach P10 gibt es keine Übergangsschicht mehr. `react-icons` und `@tabler/icons-react` sind deinstalliert, die KOMPAT-Regeln, die alten Token-Aliase und alle toten Klassen sind gelöscht, `previewEnv.dotColor` ist entfernt. Sichtbar ändert sich nichts, das Bundle wird kleiner. Die Umleitung `/tickets?woid=N` auf `/tickets/N` bleibt.
**Architecture:**
- Es wird nur gelöscht, umbenannt oder auf vorhandene Bausteine umgestellt. Die Bilder von `design-p9` sind die Referenz, ein Pixelvergleich (`vergleich.mjs`, gestartet über `run-browser.sh`) beweist nach jedem Löschschritt, dass nichts kippt.
- Reihenfolge nach Risiko: Pakete, `dotColor`, Token-Aliase (reine Umbenennung), KOMPAT, tote Klassen, Inline-Styles und Regelprüfung, Gate und Tag.
- Der Wächtertest `tests/unit/hygiene.test.mjs` hält die Ziele fest (drei Tests, je einer in Task 2, 4 und 5).
- Die Ergebnisdatei `docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md` sammelt die Belege.
**Tech Stack:** wie P1. Node 24 (`readdirSync` mit `recursive`), npm 11, ripgrep 13 ohne PCRE2 (keine PCRE2-Option, keine Lookarounds).
**Spec:** `docs/superpowers/specs/2026-10-04-weisses-design-design.md`, Abschnitte „Gestaltungsregeln“, „Design-Tokens“ (Alias-Tabelle), „Übergangsschicht KOMPAT“, „Ticket als eigene Seite / Route und Links“, „Was gleich bleibt“, „Abnahme“ und Zeile P10. Dateidetails im Anhang `…-umbaukarte.md`, Abschnitt 5 (P10), 2.2, 2.3, 4 (D) und 7. Globale Regeln, Befehlsregeln (`env.sh`, `check.sh`, `run-browser.sh`) und Schnittstellen: `2026-10-05-weisses-design-00-uebersicht.md`.
## Global Constraints
Jeder Run-Block beginnt mit `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&`. Danach gelten `$WT` (Worktree `/home/webklar/apps/ticket.webklar.com/dev-design`, Arbeitsverzeichnis), `$W` (Werkzeug) und `$TMPDIR` (Scratchpad). Tests und Build laufen nur über `"$W/check.sh"`, Browser-Skripte nur über `"$W/shots.sh"` oder `"$W/run-browser.sh"`. Suche nur mit `rg` oder `git grep`, nie mit dem Shell-`grep` auf Dateien.
Es gelten die Regeln der Übersicht. Für P10 kommt hinzu:
- Voraussetzung sind die Tags `design-p0` bis `design-p9`, alle Diff-Prüfungen vergleichen gegen `design-p9`. Erlaubt ist nur Löschen, Umbenennen und Umstellen auf vorhandene Bausteine, keine neuen Funktionen, Klassen, Texte oder Tokens. Ausnahmen: Wächtertest, Ergebnisdatei, das Werkzeug außerhalb des Repos und, nur falls sie fehlt, die Deklaration `font-variant-numeric: tabular-nums` an `.num`, `.stat .v` und `.fin-amount` (Task 7 Step 4). Neue Ansichten braucht P10 nicht, das Gate läuft über alle bestehenden.
- Nicht angefasst werden zusätzlich: die `?woid=`-Umleitung (`woidRedirectTarget` in `lib/ticketListUrl.js`, `<Navigate replace>` in `TicketsPage.jsx`), der Token-Block der Spec und die Klassen-Hooks aus „Was gleich bleibt“ (`data-testid`, `data-label`, `data-storage-done`, `data-hidden-hits`).
- Klassensuche token-genau mit `(^|[^A-Za-z0-9_-])NAME([^A-Za-z0-9_-]|$)`. `rg -w` reicht nicht, weil `-` als Wortgrenze gilt und `card` sonst `fin-card` trifft.
- `git add` mit ausdrücklichen Pfaden. Wo ein Schritt viele Dateien ändert, läuft vorher `git diff --name-only`, und `git add` bekommt genau diese geprüfte Liste.
- **Entscheidung:** Pixelinvariante. Das Aussehen der Bilder von `design-p9` gilt, jede Abweichung ist ein Fehler und wird behoben. Ausgenommen sind nur Ansichten mit Rauschen (relative Zeiten wie „vor 5 Minuten“), das ein zweiter Lauf von `design-p9` selbst zeigt.
- **Entscheidung:** Stoppregel für Icons. Liefert `rg "react-icons|@tabler" src` einen Treffer, stammt er aus einem früheren Paket oder aus Commits, die der Rebase von `origin/test` mitbrachte. P10 behebt ihn nach der Ersetzungsregel der Spec (Icon-Knopf wird Textknopf oder Textlink, Zeichen „ד „←“ „→“) in einem eigenen Commit vor Task 2 und nennt ihn in der Ergebnisdatei.
- **Entscheidung:** Die Tokens des Spec-Blocks bleiben, auch ein ungenutztes. Nur die Aliase der Spec-Tabelle fallen, `--sp-1…6` bleiben. `Button` behält die Prop `size` in der Signatur (Spec „Knöpfe“), Aufrufe mit `size="sm"` verschwinden, weil sie nichts bewirken.
- **Entscheidung:** Farbliterale in JSX sind nur in `src/components/Logo.jsx` (Markenzeichen, `#111` und `#fff` wie `public/favicon.svg`) und `src/components/serverpuls/chartColors.js` erlaubt. Inline-`<svg>` bleibt nur in `Logo.jsx` und `Sparkline.jsx`. Der Check `icon-svg` nimmt `svg.sp-spark` (P9) und `.woms-chat .chat-composer *` (P0) in `checks.mjs` aus, dazu das Gate alles unter `.woms-chat` (`ignore`, P6). P10 fügt keine Ausnahme hinzu.
- **Entscheidung:** Beim Umstellen gelten die Regeln „Bausteine benutzen“ der Übersicht und die CSS-Reihenfolge: In jedem `<Dialog as="form">` trägt jeder Knopf außer dem Absenden-Knopf `type="button"`, ein `Card` ohne Titel und Aktionen rendert kein `h2`, und Bereichs-CSS (`chat.css`, `serverpuls.css`, `pages/AdminPage.css`) lädt vor `global.css`. Überstimmt eine globale Regel es nach dem Löschen nicht mehr, braucht der Bereichs-Selektor eine höhere Spezifität, nie `!important`.
- **Entscheidung:** Die Ergebnisdatei hat sieben Abschnitte: 1 Token-Aliase, 2 KOMPAT-Inventur, 3 Tote Klassen, 4 Inline-Styles, 5 Regelprüfung und Invarianten, 6 Bundle, 7 Offene Punkte. Task 4 legt sie mit allen Überschriften an, jeder Task nimmt seinen Abschnitt in seinen Commit auf.
## Review Focus
Aus der Übersicht gehört P10 kein Punkt allein. Alle fünf laufen aber als Ansichten im Gate `p10` mit (`ticket-altdaten*`, `*-lang*`, `fehler-*`, `tasten-*`, `ticket-viel`, `tickets-suche`) und müssen unverändert bleiben. P10 verantwortet sechs eigene Prüfungen:
1. **Nichts Sichtbares ändert sich.** Pixelvergleich `design-p9` gegen P10 über alle Ansichten in allen Viewports (Standard 1440×900 und 390×844, bei `dlg-*` auch 1440×700 und 390×700), keine abweichende Ansicht außerhalb des Rauschens (Task 4, 5, 6 und 8).
2. **Keine noch gebrauchte Regel geht verloren.** Jede KOMPAT-Klasse hat eine Inventurzeile mit Nutzerzahl, jeder Restnutzer wird vor dem Löschen umgestellt (Task 5). Kein `var(--…)` zeigt ins Leere, `undefinedUsed` ist leer (Task 4).
3. **Die Icon-Pakete fehlen nirgends.** `rg` meldet 0 Treffer, der Build ist grün, kein `console-error` in irgendeiner Ansicht (ein fehlender Import wäre zur Laufzeit ein `pageerror`), das Lock passt zu `package.json` (Task 2).
4. **Die `?woid=`-Umleitung bleibt.** Ansicht `alt-link` im Gate, Test `woidRedirectTarget` aus P2 grün, `TicketsPage.jsx` und `ticketListUrl.js` unverändert (Task 8).
5. **Die Spec-Invarianten halten:** alle `data-testid`, die Zahl der `window.confirm`, Hooks und Lib-Logik nur wie in den Paketplänen geändert (Task 7 Step 5, Task 8 Review).
6. **Die übergreifenden Gestaltungsregeln gelten:** höchstens ein `.btn-primary` je `.page-head` (Spec-Regel 4, Check `max-primary` im Gate, Task 1 Step 6 und Task 8) und `tabular-nums` an Zahlen und Beträgen (Spec-Regel 6, `rg` in Task 7 Step 4).
---
### Task 1: Ausgangslage, Referenzbilder, Werkzeug
**Files** (alle unter `$W/`, kein Commit im Repo):
- Create: `vergleich.mjs`, `token-check.mjs`
- Modify: `css-klassen.mjs` (Flag `--ohne-ausnahmen`), `checks.mjs` (Check `max-primary`), `gates.mjs` (Gate `p10`)
**Interfaces:**
- Consumes: Tag `design-p9`, `build.sh`, `shots.sh`, `run-browser.sh`, `check.sh`, `css-klassen.mjs`, `farben.mjs`, `GATES.p9` (P0 bis P9).
- Produces:
- `"$W/run-browser.sh" "$W/vergleich.mjs" <A> <B> [--rauschen <A2>] [--out <Ordner>]` → JSON `{ geprueft, anders: [{ view, viewport, diffPixels, bbox }], rauschen: [view], fehlt: [view] }`. Exit 1, wenn `anders` oder `fehlt` nicht leer ist. A, B und A2 sind Ausgabeordner von `shots.sh` (`<w>x<h>/<id>.png`, z. B. `1440x900/tickets.png` und `390x844/tickets.png`).
- `node "$W/token-check.mjs" <worktree> [--fix]` → JSON `{ legacy: [{ name, ersatz, file, line, art }], undefinedUsed: [{ name, file, line }], mitFallback: [{ name, file, line }], definedUnused: [name] }`. Exit 1, wenn `legacy` oder `undefinedUsed` nicht leer ist.
- `css-klassen.mjs --ohne-ausnahmen`: ignoriert die Ausnahmeliste und gibt zusätzlich `nurAusnahme: [name]` aus, die Klassen, die nur durch die Liste als benutzt gelten.
- Check `max-primary` in `checks.mjs`: Verstoß `{ check: 'max-primary', selector: '.page-head', detail }` für jedes sichtbare `.page-head` mit mehr als einem sichtbaren `.btn-primary`.
- `GATES.p10` nach dem Schema der Übersicht (Step 6).
- [ ] **Step 1: Paketstart und Stand prüfen**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git fetch origin && git merge-base --is-ancestor design-p9 HEAD && git rebase origin/test && "$W/check.sh"
git log --oneline origin/test..origin/feature/terminplaner-tp2 -- src package.json package-lock.json tests
git tag --list 'design-p*'; git log --oneline design-p9..origin/test; git status --short
```
Expected:
- Erste Zeile: `design-p9` ist Vorfahre (sonst Exit 1: stoppen und melden, der Rebase schrieb die Pakete um), der Rebase läuft ohne Konflikt, `check.sh` endet mit Exit 0, `# fail 0`, `# pass` ist `N9` (notieren).
- Zweite Zeile (Abgleich mit `feature/terminplaner-tp2`, P10 berührt fast alle Dateien unter `src`): leer. Gibt sie etwas aus, stoppt der Agent und fragt den Eigentümer. Er empfiehlt dabei, TP2 zuerst nach `test` zu bringen.
- Tags `design-p0` bis `design-p9`, `git log` und `git status` leer. Kommen fremde Commits mit, werden ihre Dateien in den folgenden Tasks wie alle anderen geprüft.
- [ ] **Step 2: Voraussetzungen im Werkzeug**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
rg -n "GATES\.p9|p1:" "$W/gates.mjs"; rg -n "ignore" "$W/gates.mjs"; rg -n "sp-spark" "$W/checks.mjs"
ls "$W/check.sh" "$W/run-browser.sh" "$W/css-klassen.mjs" "$W/farben.mjs" "$W/build.sh" "$W/shots.sh"
```
Expected: Gates `p1` und `p9`, im `ignore` nur `.woms-chat` (P6, `.ticket-projects` strich P5), Ausnahme `svg.sp-spark` und alle sechs Dateien sind da. Fehlt etwas, wird gestoppt und gemeldet.
- [ ] **Step 3: Ausgangswerte des Bundles festhalten**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git log design-p0 --format=%B | rg "Ausgangswerte" &&
"$W/check.sh" --nur-build &&
{ find dist -type f \( -name '*.js' -o -name '*.css' -o -name '*.ttf' \) -printf '%s %p\n'; for f in dist/assets/*.js dist/assets/*.css; do echo "$f gzip=$(gzip -9 -c "$f" | wc -c)"; done; wc -l src/styles/*.css src/pages/*.css; } | tee "$W/p10-baseline-p9.txt"
```
Expected: die P0-Zeile `Ausgangswerte: <Testzahl>, JS <x> B, CSS <y> B` (Gegenprobe der Umbaukarte: JS 680.818 B, CSS 64.433 B, Lato 96.184 B, 2.079 CSS-Zeilen), Build grün, keine `.ttf` in `dist`, alle Zahlen in `$W/p10-baseline-p9.txt`.
- [ ] **Step 4: Referenzbilder und Rauschmaß**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p9-ref && "$W/shots.sh" p9-ref "$W/shots/p9-ref" --gate p9 && "$W/shots.sh" p9-ref "$W/shots/p9-ref2" --gate p9 && ls "$W/shots/p9-ref/1440x900" | wc -l`
Expected: beide Läufe Exit 0, je Ansicht ein Bild je Viewport (`1440x900` und `390x844`, bei `dlg-*` zusätzlich `1440x700` und `390x700`). Die Zahl der Ansichten ist die Sollzahl aller späteren Läufe (notieren).
- [ ] **Step 5: `vergleich.mjs` schreiben und prüfen**
Umsetzung:
- Das Skript startet Chromium selbst (Ladeweise wie `shots.mjs`) und läuft deshalb nur über `run-browser.sh`.
- Die PNGs werden in Playwright-Chromium (`about:blank`) per `createImageBitmap` und Canvas dekodiert und pixelweise verglichen. Eine andere Bildgröße ist `anders` und nennt beide Größen. `bbox` ist der kleinste Rahmen um alle abweichenden Pixel.
- `--out` bekommt je abweichender Ansicht ein Bild unter `<w>x<h>/<id>.png`, abweichende Pixel rot.
- Eine Ansicht mit Abweichung zwischen A und A2 zählt als `rauschen`, außer die Abweichung zwischen A und B reicht über deren `bbox` hinaus. Dann ist sie `anders`.
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
cp -r "$W/shots/p9-ref" "$TMPDIR/probe" && cp "$TMPDIR/probe/1440x900/tickets.png" "$TMPDIR/probe/1440x900/kunden.png"
"$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$TMPDIR/probe"; echo "exit $?"
"$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$W/shots/p9-ref2"; echo "exit $?"
```
Expected: Der erste Aufruf nennt genau `kunden` bei 1440×900 als `anders`, Exit 1. Der zweite endet mit Exit 0 oder nennt wenige Rausch-Ansichten. Jede wird einmal angesehen und in `$W/p10-rauschen.txt` notiert.
- [ ] **Step 6: `token-check.mjs`, Flag, Check `max-primary` und Gate**
Umsetzung von `token-check.mjs`:
- Es liest `src/**/*.{css,jsx,js}` und schreibt nur dort. Definiert ist ein Name durch `--name:` in CSS oder den Schlüssel `'--name'` in JSX-Objekten, benutzt durch `var(--name`.
- `legacy` enthält jede Nutzung und jede Definition eines Alias-Namens der Tabelle in Task 4 (`art` ist `nutzung` oder `definition`, `ersatz` der neue Name oder der Wert `4px` bzw. `none`).
- `undefinedUsed` sind benutzte, nirgends definierte Namen ohne Rückfallwert, mit Rückfallwert landen sie in `mitFallback`.
- `--fix` schreibt nur die `nutzung`-Einträge um (`var(--alt)` und `var(--alt, …)`), `--sidebar-w-collapsed` nie.
Dazu kommen:
- das Flag in `css-klassen.mjs`,
- der Check `max-primary` in `checks.mjs` (Spec, Gestaltungsregel 4: höchstens ein `.btn-primary` je Seitenkopf), mit `detail` `<n> .btn-primary`,
- `GATES.p10` in `gates.mjs`. P10 hat keine eigenen Ansichten, `icon-svg` gilt deshalb für alle (`'*'` ist der Präfix-Platzhalter für alle Ansichten, wie in P2), mit dem `ignore` aus `p9` unverändert. `step-failed`, `external-request` und `console-error` sind wie in jedem Gate aktiv.
```js
const iconSvg = GATES.p9.scoped['icon-svg']
GATES.p10 = {
...GATES.p9,
checks: [...GATES.p9.checks, 'max-primary'],
scoped: { ...GATES.p9.scoped, 'icon-svg': { ...iconSvg, views: ['*'] } },
}
```
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
mkdir -p "$TMPDIR/tc/src" && printf 'a{color:var(--nope);border:1px solid var(--border)}' > "$TMPDIR/tc/src/x.css"
node "$W/token-check.mjs" "$TMPDIR/tc"; echo "exit $?"
node "$W/token-check.mjs" . | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.legacy.length, j.undefinedUsed.length)"
node "$W/css-klassen.mjs" . --ohne-ausnahmen | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.dead.length, j.nurAusnahme.length)"
mkdir -p "$TMPDIR/probe-primary" && printf '<!doctype html><meta charset="utf-8"><title>Probe</title><div class="page-head"><h1>Test</h1><button class="btn btn-primary">A</button><button class="btn btn-primary">B</button></div>' > "$TMPDIR/probe-primary/index.html"
"$W/shots.sh" probe "$TMPDIR/probe-primary-out" --static "$TMPDIR/probe-primary" --only tickets --gate p10; echo "exit $?"
rg -o "max-primary" "$TMPDIR/probe-primary-out/report.json" | wc -l
```
Expected:
- Der erste Aufruf endet mit Exit 1, `undefinedUsed` nennt `--nope`, `legacy` nennt `--border` mit `ersatz` `--line`.
- Der zweite zeigt eine Zahl über 0 für `legacy` (die Alias-Definitionen stehen noch) und 0 für `undefinedUsed`.
- Der dritte nennt zwei Zahlen, sie werden notiert.
- Die Probe mit zwei Primärknöpfen in einem `.page-head` endet mit Exit 1, die letzte Zeile nennt eine Zahl über 0 (der Check `max-primary` schlägt an).
- [ ] **Step 7: Gate `p10` gegen `design-p9`**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/shots.sh" p9-ref "$W/shots/p9-ref-p10" --gate p10; echo "exit $?"`
Expected: Exit 0 (die Bilder dieses Laufs werden nicht gebraucht). Ein Treffer `icon-svg` folgt der Stoppregel für Icons. Ein Treffer `max-primary` ist ein Fehler eines früheren Pakets: Der Agent stoppt und meldet ihn dem Eigentümer, weil der überzählige Knopf das Aussehen ändern würde.
### Task 2: Icon-Pakete entfernen
**Files:**
- Create: `tests/unit/hygiene.test.mjs`
- Modify: `package.json` (Zeilen 6 und 11), `package-lock.json`
**Interfaces:**
- Consumes: nichts. Voraussetzung ist `rg "react-icons|@tabler" src` = 0.
- Produces: ein Lock ohne `@tabler/icons-react`, `@tabler/icons` und `react-icons`, dazu `hygiene.test.mjs` mit den Helfern `files`, `read`, `offenders(re, only)`, auf denen Task 4 und 5 aufbauen.
- [ ] **Step 1: Failing Test schreiben**
```js
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync, readdirSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..')
const files = readdirSync(path.join(root, 'src'), { recursive: true })
.filter((f) => /\.(jsx?|css)$/.test(f))
.map((f) => path.join('src', f))
const read = (rel) => readFileSync(path.join(root, rel), 'utf8')
const offenders = (re, only = /./) => files.filter((f) => only.test(f) && re.test(read(f)))
test('Icon-Pakete sind weder Abhängigkeit noch Import', () => {
const pkg = JSON.parse(read('package.json'))
for (const n of ['react-icons', '@tabler/icons-react']) assert.equal(pkg.dependencies[n], undefined, n)
assert.deepEqual(offenders(/react-icons|@tabler/), [])
})
```
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/hygiene.test.mjs`
Expected: FAIL (`package.json` führt beide Pakete).
- [ ] **Step 2: Beweisen, dass nichts mehr importiert**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|@tabler" src`
Expected: keine Ausgabe. Bei einem Treffer gilt die Stoppregel der Global Constraints.
- [ ] **Step 3: Deinstallieren**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0 npm uninstall react-icons @tabler/icons-react --no-audit --no-fund`
Expected: Exit 0.
- [ ] **Step 4: Lock und Index prüfen**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git status --short; git diff package.json | rg "^[-+] "; rg -c "tabler|react-icons" package.json package-lock.json; git diff --numstat package-lock.json
npm ls react-icons @tabler/icons-react 2>&1 | rg "empty"; npm ci --dry-run --no-audit --no-fund; echo "exit $?"
```
Expected:
- `git status` zeigt genau `M package-lock.json`, `M package.json` und `?? tests/unit/hygiene.test.mjs`, keine `D`-Zeile (`node_modules` ist nicht getrackt).
- Der Diff von `package.json` hat genau zwei `-`-Zeilen, `rg -c` gibt nichts aus.
- `numstat` zeigt 0 hinzugefügte Zeilen. Stehen dort wenige, sind es höchstens geänderte Flags (`"peer"`, `"dev"`), und jede `+`-Zeile wird angesehen.
- `npm ls` meldet `(empty)`, `npm ci --dry-run` endet mit Exit 0.
- [ ] **Step 5: Tests und Build**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh"`
Expected: Exit 0, `# fail 0`, `# pass` = `N9 + 1`, Build grün.
- [ ] **Step 6: Commit**
```bash
git add package.json package-lock.json tests/unit/hygiene.test.mjs
git commit -m "Aufraeumen: react-icons und @tabler/icons-react deinstalliert (src ohne Icon-Importe), Waechtertest" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 3: `dotColor` und Icon-Kommentare
**Files:**
- Modify: `src/lib/previewEnv.js` (Zeile 1 und Zeilen 15–18 laut Ausgangsstand), `tests/unit/previewEnv.test.mjs` (Zeile 5 und der Test `dotColor`, Zeilen 21–29)
- Modify: `src/lib/navLinks.js` (Zeile 1), `tests/unit/navLinks.test.mjs` (Zeile 5)
**Interfaces:**
- Consumes: `isValidEnvName`, `buildEnvPayload`, `isDevPreview`, `envValueTooLong`, `MAX_ENV_VARS` aus `previewEnv.js`, unverändert.
- Produces: `previewEnv.js` ohne `dotColor` und `DOT_COLORS`.
- [ ] **Step 1: Beweisen, dass `dotColor` unbenutzt ist**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "dotColor|DOT_COLORS" src tests`
Expected: Treffer nur in `src/lib/previewEnv.js` und `tests/unit/previewEnv.test.mjs` (P5 hat `PreviewBlock` und `ServicesPage` umgestellt). Weitere Treffer sind ein Fehler eines früheren Pakets.
- [ ] **Step 2: Test zuerst anpassen**
Der Test `dotColor` und der Name im `loadModule`-Destrukturieren entfallen.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/previewEnv.test.mjs`
Expected: PASS mit 4 Tests (`isValidEnvName`, `buildEnvPayload`, `isDevPreview`, `envValueTooLong`).
- [ ] **Step 3: Funktion löschen**
`DOT_COLORS` und `dotColor` fallen, der Kopfkommentar heißt „(Variablen, Dev-Erkennung)“.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/previewEnv.test.mjs; rg -n "dotColor|DOT_COLORS|#10b981|#f59e0b|#a0aec0" src tests`
Expected: PASS, keine Treffer.
- [ ] **Step 4: Icon-Kommentare**
- `src/lib/navLinks.js` Zeile 1 wird `// Eintraege der Sidebar als reine Daten (unter Node testbar); Navbar.jsx rendert sie als Textliste.` Der zweite Kommentarsatz zu Unterpunkten und `count` bleibt.
- `tests/unit/navLinks.test.mjs` Zeile 5 wird `// Sidebar: Eintraege als reine Daten (Navbar.jsx rendert sie als Textliste)`.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n -i "(^|[^a-z])icons?([^a-z]|$)" src tests --glob '!*.css' --glob '!hygiene.test.mjs'`
Expected: keine Treffer, höchstens Kommentare zum fremden Chat-Baustein in `src/components/chat` und `src/lib/loadChatComposer.js`. Jeder andere Treffer wird umformuliert. „favicon“ und der Wächtertest zählen nicht.
- [ ] **Step 5: Test**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" --nur-tests`
Expected: Exit 0, `# fail 0`, `# pass` = `N9` (hygiene +1, `dotColor` −1).
- [ ] **Step 6: Commit**
```bash
git add src/lib/previewEnv.js tests/unit/previewEnv.test.mjs src/lib/navLinks.js tests/unit/navLinks.test.mjs
git commit -m "Aufraeumen: previewEnv.dotColor samt Test entfernt, Icon-Kommentare in navLinks angepasst" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 4: Token-Aliase abbauen
**Files:**
- Modify: `src/styles/global.css` (Alias-Block in `:root`), alle Dateien unter `src/` mit alten Namen (Liste aus Step 3)
- Create: `docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md` (Überschriften 1 bis 7, Abschnitt 1 gefüllt)
- Test: `tests/unit/hygiene.test.mjs` (zweiter Test)
**Interfaces:**
- Consumes: `$W/token-check.mjs` (Task 1) und die Alias-Tabelle der Spec:
| Alter Name | Neu |
|---|---|
| `--surface-1`, `--surface-3`, `--surface`, `--dark-bg`, `--dark-card-bg` | `--bg` |
| `--surface-2`, `--surface-hover` | `--bg-hover` |
| `--border`, `--gray-700`, `--admin-line` | `--line` |
| `--border-strong`, `--admin-line-strong` | `--line-strong` |
| `--border-accent` | `--accent-ring` |
| `--text-muted`, `--text-faint`, `--gray-800` | `--text-2` |
| `--dark-text`, `--gray-900` | `--text` |
| `--accent-2`, `--green-secondary` | `--accent-hover` |
| `--green-primary` | `--accent` |
| `--info`, `--warn`, `--ok` | `--blue-fg`, `--orange-fg`, `--green-fg` |
| `--r-sm`, `--r-md`, `--r-lg`, `--r-pill` | `--radius`, `--radius-lg`, `--radius-lg`, Wert `4px` |
| `--shadow-1`, `--shadow-2`, `--shadow-3` | `none`, `--shadow-btn`, `--shadow-pop` |
| `--sidebar-w-collapsed` | entfällt, kein Nutzer erlaubt |
- Produces: `:root` ohne Aliase, `token-check` meldet `legacy` und `undefinedUsed` leer.
- [ ] **Step 1: Failing Test ergänzen**
```js
const LEGACY = ['surface-1', 'surface-2', 'surface-3', 'surface-hover', 'surface', 'dark-bg', 'dark-card-bg', 'border', 'border-strong', 'border-accent',
'gray-700', 'gray-800', 'gray-900', 'admin-line', 'admin-line-strong', 'text-muted', 'text-faint', 'dark-text', 'accent-2', 'green-secondary',
'green-primary', 'info', 'warn', 'ok', 'r-sm', 'r-md', 'r-lg', 'r-pill', 'shadow-1', 'shadow-2', 'shadow-3', 'sidebar-w-collapsed']
test('keine alten Token-Namen mehr (Aliase sind gelöscht)', () => {
const re = new RegExp(`(^|[^A-Za-z0-9-])--(${LEGACY.join('|')})([^A-Za-z0-9-]|$)`)
assert.deepEqual(offenders(re), [])
})
```
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/hygiene.test.mjs`
Expected: FAIL im neuen Test.
- [ ] **Step 2: Bestandsaufnahme**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
node "$W/token-check.mjs" . > "$TMPDIR/token-vorher.json"; node -e "const j=JSON.parse(require('fs').readFileSync(process.argv[1]));const c={};for(const e of j.legacy)c[e.name+' '+e.art]=(c[e.name+' '+e.art]||0)+1;console.log(c)" "$TMPDIR/token-vorher.json"
```
Expected: Zahl der Nutzungen und Definitionen je Alias. Sie kommt in Abschnitt 1 der Ergebnisdatei (Spalten: alter Name, Nutzungen vorher, ersetzt durch).
- [ ] **Step 3: Nutzungen umbenennen**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/token-check.mjs" . --fix; git diff --name-only`
Expected: Es ändern sich nur `.css`, `.jsx` und `.js` unter `src/`. Das ist eine reine Umbenennung, der Wert jedes Tokens bleibt gleich.
- [ ] **Step 4: Alias-Block löschen**
In `global.css` fallen der Alias-Block hinter dem Token-Block samt Kommentar und jede übrige Definition eines Alias-Namens, auch außerhalb von `:root`.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/hygiene.test.mjs && node "$W/token-check.mjs" . | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.legacy.length, j.undefinedUsed.length, j.mitFallback.length)"`
Expected: PASS und `0 0` plus eine Zahl für `mitFallback`. Jeder Eintrag dort wird angesehen: Der Rückfallwert fällt, wenn das Token existiert, sonst steht der Eintrag in Abschnitt 7.
- [ ] **Step 5: Bauen, Gate, Pixelvergleich**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p10 && "$W/shots.sh" p10 "$W/shots/p10-t4" --gate p10 && "$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$W/shots/p10-t4" --rauschen "$W/shots/p9-ref2" --out "$W/shots/p10-t4-diff"`
Expected: Gate Exit 0, Vergleich Exit 0. Weicht eine Ansicht ab, hat `--fix` einen Namen falsch ersetzt oder eine Definition lag in einer Datei, die das Skript nicht liest. Das wird behoben.
- [ ] **Step 6: Ergebnisdatei und Commit**
Die Ergebnisdatei wird mit den Überschriften 1 bis 7 angelegt, Abschnitt 1 bekommt die Tabelle aus Step 2.
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git diff --name-only
git add docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md tests/unit/hygiene.test.mjs <die geprüften Dateien aus der Liste oben>
git commit -m "Aufraeumen: Token-Aliase der Altnamen durch die neuen Tokens ersetzt und geloescht" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 5: KOMPAT-Block entfernen
**Files:**
- Modify: `src/styles/global.css` (Block `/* ===== KOMPAT (bis P10) ===== */` und der Gliederungskommentar am Dateianfang), Dateien mit Restnutzern (Liste aus Step 3)
- Modify: Ergebnisdatei Abschnitt 2
- Test: `tests/unit/hygiene.test.mjs` (dritter Test)
**Interfaces:**
- Consumes: die Bausteine aus P1 (`.btn`, `.btn-primary`, `.btn-quiet`, `.link`, `.link-danger`, `.badge`, `.field`, `.input`, `.select`, `.textarea`, `.msg`, `.muted`, `.dialog`, `.overlay`, `.seg button.on`) und `Dialog`, `Card`, `Badge`, `StatusPill`.
- Produces: `global.css` ohne KOMPAT. Kein Selektor der Spec-Tabelle „Übergangsschicht KOMPAT“ ist übrig, `.page` bleibt als Wurzelklasse der Seiten.
- [ ] **Step 1: Failing Test ergänzen**
```js
test('keine Übergangsschicht und kein !important im CSS', () => {
assert.deepEqual(offenders(/KOMPAT|!important/, /\.css$/), [])
})
```
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/hygiene.test.mjs`
Expected: FAIL im neuen Test.
- [ ] **Step 2: Grenzen und Inventur**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
read A B < <(awk '/KOMPAT \(bis P10\)/{a=NR; next} a && /^\/\* (=====|---)/{print a+1, NR-1; f=1; exit} END{if(a&&!f)print a+1, NR}' src/styles/global.css); echo "KOMPAT $A-$B"
sed -n "${A},${B}p" src/styles/global.css | rg -o "\.[a-z][a-z0-9_-]*" | sort -u | sed 's/^\.//' | while read c; do echo "$(rg -l "(^|[^A-Za-z0-9_-])${c}([^A-Za-z0-9_-]|$)" src --glob '!*.css' | wc -l) $c"; done | sort -n | tee "$TMPDIR/kompat-inventur.txt"
```
Expected:
- Der Block endet an der nächsten Kopfzeile `/* =====` oder `/* ---`, sonst am Dateiende.
- Die Inventur hat 0 Nutzer für jede Klasse der Tabelle in Step 3. Bekannte Ausnahmen: `overlay` (nur `src/components/ui/Dialog.jsx`), `row` (nur an `<tr>`) und Klassen, die auch ein neuer Baustein trägt (`btn`, `badge`, `field`, `link`). Deren Regel wandert nach Step 4 in Block 3.
- Die Liste kommt als Tabelle (Klasse, Nutzer, Entscheidung) in Abschnitt 2 der Ergebnisdatei.
- [ ] **Step 3: Restnutzer finden und umstellen**
| KOMPAT-Zeile der Spec | Gesuchte Namen | Ersatz |
|---|---|---|
| `.ui-card*` | `ui-card` | `Card` (ohne Titel und Aktionen rendert es kein `h2`) oder `<section>` mit `h2.section` |
| `.ui-btn*` | `ui-btn`, `-primary`, `-ghost`, `-danger`, `-sm`, Muster `ui-btn-${…}` | `btn`, `btn btn-primary`, `link-danger`, Zeilenaktion `link` |
| `.form-group`, `.form-label`, `.form-control` | dieselben drei | `field`, Label im `.field`, `input`, `select`, `textarea` |
| `.badge-*`, `.status-*`, `.priority-*` | `badge-` und `status-` mit `muted`, `info`, `ok`, `warn`, `danger`, `open`, `working`, `awaiting`, `assigned`, `closed`; `priority-critical`; Muster `badge-${…}`, `status-${…}` | `Badge`, `StatusPill` |
| `.overlay` (Altform), `.overlay-content`, `.overlay-close` | `overlay-content`, `overlay-close`, `overlay` außerhalb von `Dialog.jsx`, `fin-dialog` (760 px, laut P7 tot) | `Dialog` |
| `.seg button.active` | kein festes Muster: jedes `seg` in JSX ansehen | Knopf mit `on` und `aria-pressed` |
| `.bg-red`, `.text-white`, `.text-grey`, `.text-red` | dieselben vier | `msg err` bzw. `muted` |
| `.side-count`, `.side-logo` | dieselben zwei | schon entfernt: `.side-logo` in P4, `.side-count` in P6. Ein Treffer ist ein Rest und wird entfernt |
| `.btn-dark`, `.btn-teal` | dazu `btn-green` | `btn btn-primary` |
| `.card*`, `.row`, `.col*`, `.dropdown*` | `card`, `card-header`, `card-body`, `col`, `col-6`, `dropdown*`, `row` außerhalb von `<tr>` | entfallen |
| `.spinner` | `spinner` | Text „Wird geladen …“ |
| `.page .page`, `.page .main-content` | `main-content` | entfällt (Step 5) |
| Links in Bestandsklassen | `fin-link`, `credit-cols`, `area-rows`. Die Klasse `legal-files` bleibt (P8), nur die Regel `.legal-files a` fällt. | Klasse `link` am `<a>`, sonst die Regel in den Block des Nutzers |
In jedem `<Dialog as="form">`, das dabei entsteht oder angefasst wird, trägt jeder Knopf außer dem Absenden-Knopf `type="button"`.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n '(^|[^A-Za-z0-9_-])(ui-(card|btn)|form-(group|label|control)|(badge|status)-(muted|info|ok|warn|danger|open|working|awaiting|assigned|closed)|priority-critical|overlay-(content|close)|fin-dialog|bg-red|text-(white|grey|red)|side-(count|logo)|btn-(dark|teal|green)|spinner|dropdown[a-z-]*|card|card-header|card-body|col|col-6|main-content|fin-link|credit-cols|area-rows)([^A-Za-z0-9_-]|$)' src --glob '!*.css'; rg -n '(badge|status|ui-btn)-\$\{' src; rg -n 'className="[^"]*(^|[^A-Za-z0-9_-])row([^A-Za-z0-9_-]|")' src | rg -v '<tr'`
Expected: keine Treffer. Ein Treffer in einem Kommentar wird umformuliert, jeder Code-Treffer nach der Tabelle umgestellt, eine Gruppe je Commit (Meldung „Aufraeumen: Restnutzer von <Gruppe> auf <Baustein> umgestellt“).
- [ ] **Step 4: Regeln, die ein Baustein noch braucht**
Entscheidung: Das Aussehen der Bilder von `design-p9` gilt. Eine KOMPAT-Regel, deren Selektor auch einen neuen Baustein trifft, wird nicht gelöscht. Ihre Deklarationen gehören dann an den Baustein in Block 3, nur die alten Selektoren fallen. Bekannter Fall ist `.overlay`, die Hülle von `Dialog`:
- Ist die KOMPAT-Zeile für die Altform ein eigener Selektor (`overlay-content`, `:has(…)`), fällt sie weg.
- Hat sie die Basisregel `.overlay` selbst verändert (`overflow-y: auto`, Abstand 24/16 px), gelten die Werte, die die Bilder von `design-p9` für `Dialog` zeigen, und bleiben in der Basisregel.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "^\.(overlay|dialog)\b" src/styles/global.css`
Expected: eine Basisregel `.overlay` mit der Mobilregel bei 860 px, ohne Verweis auf `overlay-content`.
- [ ] **Step 5: Block löschen, Verschachtelung statisch beweisen**
Der Block samt Marker fällt, der Gliederungskommentar nennt danach vier Blöcke statt fünf.
Entscheidung: `.page` bleibt als Wurzelklasse jeder Seite. Gelöscht werden nur die Verschachtelungsregel `.page .page, .page .main-content` und die alte `.main-content`-Regel.
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
node --test tests/unit/hygiene.test.mjs
rg -n "KOMPAT|main-content" src
rg -n 'className=\{?["`]([^"`]* )?page([ "`}]|$)' src --glob '*.jsx' | rg -v '^src/pages/'
rg -n '<[A-Za-z]+Page\b' src --glob '!App.jsx'
```
Expected: PASS und drei leere Ausgaben. Es gibt also kein `KOMPAT` und kein `main-content` mehr, die Klasse `page` steht nur in `src/pages/*.jsx` (der Treffer-Filter erfasst nur Klassenstellen, keine Props wie `page` in `Pager`), und nur `App.jsx` rendert `*Page`-Komponenten. Eine Seite in einer Seite ist damit ausgeschlossen. Ein Treffer, der nur ein Wort im Kommentar ist, wird umformuliert.
- [ ] **Step 6: Bauen, Gate, Pixelvergleich**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p10 && "$W/shots.sh" p10 "$W/shots/p10-t5" --gate p10 && "$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$W/shots/p10-t5" --rauschen "$W/shots/p9-ref2" --out "$W/shots/p10-t5-diff"`
Expected: Gate Exit 0, Vergleich Exit 0. Auch alle Alt-Dialoge (`dlg-*`), die `.seg`-Ansichten (`finanzen`, `serverpuls`) und Links bleiben unverändert. Weicht eine Ansicht ab, wird das Diff-Bild angesehen. Es gibt zwei Ursachen:
- Die gelöschte Regel gehört an die Stelle ihres Bausteins (Step 4).
- Bereichs-CSS (`chat.css`, `serverpuls.css`, `pages/AdminPage.css`) lädt vor `global.css` und wurde bisher von KOMPAT überstimmt. Der Bereichs-Selektor bekommt dann eine höhere Spezifität, nie `!important`.
- [ ] **Step 7: Commit**
Abschnitt 2 der Ergebnisdatei ist gefüllt.
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git diff --name-only
git add docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md tests/unit/hygiene.test.mjs src/styles/global.css <weitere geprüfte Dateien>
git commit -m "Aufraeumen: KOMPAT-Block, .page-Verschachtelungsregel und Altklassen entfernt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 6: Tote Klassen und Keyframes
**Files:**
- Modify: `src/styles/*.css`, `src/pages/*.css`, JSX-Dateien mit Klassen ohne Regel, Ergebnisdatei Abschnitt 3, `$W/css-klassen.mjs` (Ausnahmeliste)
- Delete: CSS-Dateien ohne verbleibende Regel samt Importzeilen
**Interfaces:**
- Consumes: `css-klassen.mjs` mit `--ohne-ausnahmen` (Task 1).
- Produces: `dead` und `undefinedUsed` sind leer, die Ausnahmeliste enthält nur Namen mit belegter Konstruktionsstelle.
- [ ] **Step 1: Liste erzeugen**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
node "$W/css-klassen.mjs" . --ohne-ausnahmen > "$TMPDIR/klassen-p10.json"; node -e "const j=JSON.parse(require('fs').readFileSync(process.argv[1]));for(const k of ['dead','undefinedUsed','nurAusnahme'])console.log(k, j[k].length+':', j[k].join(' '))" "$TMPDIR/klassen-p10.json"
```
Expected: drei Zeilen mit Namen. `dead` enthält zunächst auch die Namen aus `nurAusnahme`.
- [ ] **Step 2: Gegenprobe und Löschen**
Für jeden Namen aus `dead`, der nicht in `nurAusnahme` steht:
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "(^|[^A-Za-z0-9_-])NAME([^A-Za-z0-9_-]|$)" src --glob '!*.css'`
Expected: keine Treffer. Dann fällt jede Regel mit diesem Namen in der definierenden Datei, in einer Selektorliste nur der Name.
Für jeden Namen aus `nurAusnahme` bekommt Abschnitt 3 der Ergebnisdatei eine Zeile mit der Stelle, die ihn baut: ein Template-Präfix wie `fin-fixed-${`, eine Zeichenkette in `lib/*.js` oder der Fremd-Composer bei `chat-*`. Ohne Beleg gilt die Klasse als tot und fällt.
- [ ] **Step 3: Klassen ohne Regel, `size`, Keyframes**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
rg -n 'size="sm"' src; rg -n '(^|[^A-Za-z0-9_-])(btn-sm|p-4|status-filter|supporters-editor|timeline)([^A-Za-z0-9_-]|$)' src --glob '!*.css'
rg -o "@keyframes [A-Za-z0-9_-]+" src --glob '*.css' | sed 's/.*@keyframes //' | sort -u | while read k; do rg -q "animation[^;]*(^|[^A-Za-z0-9_-])${k}([^A-Za-z0-9_-]|;)" src --glob '*.css' || echo "TOT $k"; done
```
Expected: alle drei Ausgaben leer. Treffer der ersten beiden Befehle werden aus dem JSX entfernt (die Klassen haben keine Regel, `size` bewirkt nichts). Ein toter Keyframe fällt samt Regel. Zu erwarten ist `spin`, wenn `.spin` (Notiz aus P8) und `.spinner` keinen Aufrufer mehr haben.
- [ ] **Step 4: Leere Dateien, Ausnahmeliste, Schriftordner**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && wc -l src/styles/*.css src/pages/*.css; rg -n "admin-areas" src index.html; ls src/assets 2>&1 | head -1; rg -n -i "assets/fonts|lato" src index.html`
Expected:
- Eine CSS-Datei ohne Regel wird mit `git rm` gelöscht, ihre `import`-Zeilen verschwinden (`rg -l "<Dateiname>" src`). `admin-areas.css` hat P6 schon gelöscht, ein Treffer von `rg -n "admin-areas" src index.html` ist ein Rest und wird entfernt.
- `src/assets` existiert nicht, das letzte `rg` hat keine Treffer.
- Die Ausnahmeliste in `$W/css-klassen.mjs` enthält nur noch die Namen mit Beleg aus Step 2. `badge-*`, `status-*`, `ticket-tab-active`, `ui-btn-*` und `side-dot-*` fallen.
- [ ] **Step 5: Prüfen**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/css-klassen.mjs" . | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.dead.length, j.undefinedUsed.length)" && "$W/check.sh" && "$W/build.sh" "$WT" p10 && "$W/shots.sh" p10 "$W/shots/p10-t6" --gate p10 && "$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$W/shots/p10-t6" --rauschen "$W/shots/p9-ref2" --out "$W/shots/p10-t6-diff"`
Expected: `0 0`, `check.sh` Exit 0 mit `# fail 0` und `# pass` = `N9 + 2`, Gate Exit 0, Vergleich Exit 0.
- [ ] **Step 6: Commit**
Abschnitt 3 der Ergebnisdatei hat die Tabelle: Klasse, definierende Datei, Beleg oder „gelöscht“.
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git status --short
git add docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md <die geprüften Dateien>
git commit -m "Aufraeumen: tote Klassen, Keyframes und leere CSS-Dateien geloescht, Klassen ohne Regel aus dem JSX entfernt" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 7: Inline-Styles, Regelprüfung, Invarianten
**Files:**
- Modify: JSX-Dateien mit Inline-Styles ohne Laufzeitwert (Ersatz sind vorhandene Klassen, neue kommen nicht dazu), Ergebnisdatei Abschnitte 4, 5 und 7
**Interfaces:**
- Consumes: `$W/farben.mjs` (P1) und der Abschnitt „Bewusst offen“ in `docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md`.
- Produces: eine begründete Liste aller `style=`-Stellen und die Regelprüfung der Spec als belegte Liste.
- [ ] **Step 1: Inline-Styles nur noch für Laufzeitwerte**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -nU "style=\{\{(\s*[A-Za-z]+:\s*('[^']*'|\"[^\"]*\"|-?[0-9]+(\.[0-9]+)?)\s*,?)+\s*\}\}" src --glob '*.jsx'`
Expected: keine Treffer. Das Muster findet jedes Objekt, dessen Werte alle feste Zahlen oder Zeichenketten sind. Jeder Treffer wird durch eine vorhandene Klasse ersetzt.
- [ ] **Step 2: Rest begründen**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "style=\{" src --glob '*.jsx'`
Expected: wenige Zeilen. Abschnitt 4 der Ergebnisdatei bekommt je Zeile Datei:Zeile, Ausdruck und Begründung. Erlaubt sind ein aus Daten oder Messung berechneter Wert (Breite in Prozent, Höhe, Spaltenzahl) und das Durchreichen der `style`-Prop in `Badge.jsx`. Feste Abstände, Farben, Schrift, Flex und Größen sind nicht erlaubt, solche Stellen werden umgestellt, und Step 1 beginnt von vorn.
- [ ] **Step 3: Farben**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n '#[0-9a-fA-F]{3,8}\b|rgba?\(|hsla?\(' src --glob '*.jsx' --glob '*.js'; node "$W/farben.mjs" .`
Expected:
- Der `rg` nennt nur `src/components/Logo.jsx` und `src/components/serverpuls/chartColors.js`.
- `farben.mjs` listet kein Literal unter 3:1 auf Weiß, keine dunkle Schrift auf `--accent` und kein Weiß-Alpha.
- Jede Zeile des Abschnitts „Bewusst offen“ in `p1-tote-klassen.md` ist erledigt. Hat sie das Zielpaket P10, wird sie hier behoben und in Abschnitt 5 genannt.
- [ ] **Step 4: Regelprüfung der Spec**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
rg -n "!important" src # 1
rg -n -e "gradient\(" -e "backdrop-filter" -e "filter: *blur" src --glob '*.css' # 2
rg -n "text-transform" src --glob '*.css' # 3
rg -n "font-weight: *(800|900)" src # 4
rg -n -e "Lato" -e "@font-face" src index.html # 5
rg -n "font-family" src --glob '*.css' # 6
rg -n -e "color-scheme" -e "data-theme" -e "data-accent" src index.html # 7
rg -no -e "@media[^{]*" -e "@container[^{]*" src --glob '*.css' # 8
rg -n "<svg" src --glob '*.jsx' # 9
rg -n -e "Kpi" -e "kpi" src # 10
rg -n -e "<title>" -e 'rel="icon"' index.html # 11
for s in '\.num' '\.stat \.v' '\.fin-amount'; do printf '%s: ' "$s"; rg -l -U "$s([^A-Za-z0-9_{}-][^{}]*)?\{[^}]*tabular-nums" src --glob '*.css' | tr '\n' ' '; echo; done # 12
```
Expected:
- 1, 4, 5 und 10: leer.
- 2: höchstens eine Zeile, der Chevron von `.select`. 3: eine Zeile, die Wochentage `.wd` im Planboard.
- 6: nur `var(--font)` und der Monospace-Stapel (`ui-monospace`). 7: nur `color-scheme: light`.
- 8: nur `max-width: 1100px` und `860px`, bei `@container` 560, 640 und 860 px, dazu `prefers-reduced-motion`. Zugelassen ist außerdem der eine Block `max-width: 640px` in `src/styles/chat.css`: Er setzt im Eingabefeld des fremden Portal-Composers wieder `font-size: 16px` (gegen das iOS-Zoomen, P6) und ist die WOMS-eigene Überschreibung des Composers.
- 9: nur `Logo.jsx` und `Sparkline.jsx`. 11: `Webklar WOMS 2.0` und die Zeile für `/favicon.svg`.
- 12 (Spec-Regel 6, Zahlen und Beträge): drei Zeilen, jede nennt mindestens `src/styles/global.css`. Fehlt bei einem Selektor die Datei, trägt seine Regel kein `font-variant-numeric: tabular-nums`. P10 ergänzt dann genau diese Deklaration an der bestehenden Regel. Das ist die einzige erlaubte Abweichung von der Pixelinvariante. Die Ansichten, die dadurch im Vergleich von Task 8 abweichen, stehen mit Begründung in Abschnitt 5.
- Spec-Regel 4 (höchstens ein schwarzer Knopf je Seitenkopf) prüft kein `rg`, sondern der Check `max-primary` im Gate `p10` (Task 1 Step 6, Lauf in Task 8 Step 2).
Seitenbreakpoints außerhalb von 860 und 1100 px werden auf diese Werte gezogen (Spec, Regel 7), ausgenommen der 640-px-Block in `chat.css`. Jede andere Abweichung steht mit Begründung in Abschnitt 7. Befehl, Ausgabe und Urteil jeder Zeile stehen in Abschnitt 5.
- [ ] **Step 5: Invarianten gegen `design-p0`**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
git grep -ho 'data-testid=[^ >]*' design-p0 -- src | sort -u > "$TMPDIR/ti-p0.txt"
rg -oN --no-filename 'data-testid=[^ >]*' src | sort -u > "$TMPDIR/ti-jetzt.txt"
diff "$TMPDIR/ti-p0.txt" "$TMPDIR/ti-jetzt.txt" | rg '^<'
echo "confirm: $(git grep -h 'window\.confirm' design-p0 -- src | wc -l) -> $(rg --no-filename 'window\.confirm' src | wc -l)"
git diff --name-status design-p0..HEAD -- src/hooks src/lib
```
Expected:
- Das `diff` zeigt keine `<`-Zeile, es ging keine `data-testid` verloren. Neue (`>`) sind erlaubt und werden genannt.
- Die Zahl der `window.confirm` ist gleich.
- In `src/hooks` steht nur `M useWorkorders.js` plus die neuen `useWorkorder.js`, `useTicketCounts.js`, `useTicketMinutes.js`. In `src/lib` stehen nur `D sidebar.js`, neue Dateien (darunter `rowClick.js` aus P2) und `M` für `ticketStatus.js`, `navLinks.js`, `previewEnv.js`, `serverpulsFormat.js`, `creditFormat.js`, `offerFormat.js`, `storageFormat.js`, `projectList.js`, `leads.js`. Jede andere `M`- oder `D`-Zeile ist ein Befund.
Die Ausgaben stehen in Abschnitt 5.
- [ ] **Step 6: Commit**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" --nur-tests && git status --short`
Expected: Exit 0, `# fail 0`, `# pass` = `N9 + 2`, nur die erwarteten Änderungen.
```bash
git add docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md <die geprüften Dateien aus Step 1 bis 4>
git commit -m "Aufraeumen: Inline-Styles nur noch fuer Laufzeitwerte, Regelpruefung und Invarianten dokumentiert" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 8: Gate P10, Bundle, Review, Tag
- [ ] **Step 1: Rebase**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git merge-base --is-ancestor design-p9 HEAD && git rebase origin/test && "$W/check.sh"`
Expected: `design-p9` ist Vorfahre, der Rebase läuft ohne Konflikt, Exit 0 mit `# fail 0` und `# pass` = `N9 + 2`. Gate, Bilder und Review beziehen sich damit auf den Stand, der getaggt wird.
- [ ] **Step 2: Vollständiger Lauf und Pixelvergleich**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p10 && "$W/shots.sh" p10 "$W/shots/p10" --gate p10 && "$W/run-browser.sh" "$W/vergleich.mjs" "$W/shots/p9-ref" "$W/shots/p10" --rauschen "$W/shots/p9-ref2" --out "$W/shots/p10-diff"`
Expected:
- Das Gate `p10` endet mit Exit 0. Über alle Ansichten, auch die der Review-Focus-Punkte 1 bis 5 der Übersicht, gibt es keine Verstöße gegen `external-request`, `console-error`, `dark-surface`, `low-contrast`, `overflow-x`, `icon-svg` (außer unter `.woms-chat`), `max-primary` und `step-failed`.
- Der Vergleich endet mit Exit 0, `fehlt` ist leer, die Zahl der Ansichten gleicht der Sollzahl aus Task 1 Step 4. Rausch-Ansichten stehen in `$W/p10-rauschen.txt`.
- Die Ansicht `alt-link` hat keinen Verstoß. Ihr Schritt prüft, dass `/tickets?woid=100131&status=done` auf `/tickets/100131` endet.
- [ ] **Step 3: Bundle-Größe**
Run:
```bash
source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh &&
"$W/check.sh" --nur-build &&
{ find dist -type f \( -name '*.js' -o -name '*.css' -o -name '*.ttf' \) -printf '%s %p\n'; for f in dist/assets/*.js dist/assets/*.css; do echo "$f gzip=$(gzip -9 -c "$f" | wc -c)"; done; wc -l src/styles/*.css src/pages/*.css; } | tee "$W/p10-baseline-p10.txt"
```
Expected:
- Abschnitt 6 der Ergebnisdatei bekommt eine Tabelle mit den Zeilen JS in Byte, JS gzip, CSS in Byte, CSS gzip, Schrift in Byte, CSS-Zeilen und Testzahl und den Spalten P0, `design-p9`, P10. Die P0-Werte stammen aus der Commit-Zeile `Ausgangswerte` (Gegenprobe: Umbaukarte), die P9-Werte aus `$W/p10-baseline-p9.txt`.
- CSS und CSS-Zeilen sind kleiner als bei `design-p9`, JS ist nicht größer. Gegenüber P0 fehlen 96.184 B Schrift. Werte, die gegenüber P0 wachsen, stehen in Abschnitt 7.
- [ ] **Step 4: Ergebnisdatei abschließen**
Abschnitt 7 (Offene Punkte) ist gefüllt, auch wenn er nur „keine“ sagt.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git status --short`
Expected: nur `M docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md`.
```bash
git add docs/superpowers/plans/2026-10-05-weisses-design-p10-ergebnisse.md
git commit -m "Aufraeumen: Ergebnisdatei mit Bundle-Vergleich und offenen Punkten" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
- [ ] **Step 5: Code-Review (sonnet)**
Geprüft wird der Diff `design-p9..HEAD` gegen die Spec-Abschnitte „Übergangsschicht KOMPAT“, „Gestaltungsregeln“ und „Was gleich bleibt“, dazu liest der Prüfer die Ergebnisdatei. Schwerpunkte:
- Der Diff enthält nur Löschungen, Umbenennungen und Umstellungen auf vorhandene Bausteine, keine neuen Texte, Klassen oder Tokens außer dem Wächtertest.
- Zehn Stichproben aus Abschnitt 2 (KOMPAT) und 3 (tote Klassen): Die Klasse hat per `rg` keinen Nutzer, ihre Regel ist weg.
- `package.json` und Lock haben genau `@tabler/icons-react`, `@tabler/icons` und `react-icons` verloren.
- `git diff design-p9 -- src/pages/TicketsPage.jsx src/lib/ticketListUrl.js` ist leer oder enthält nur Kommentare, `woidRedirectTarget` ist erhalten.
- Jede Begründung in Abschnitt 4 stimmt, die Invarianten aus Abschnitt 5 sind erfüllt.
Wichtige Befunde werden behoben, danach laufen Step 1 bis 3 erneut. Kleinigkeiten kommen in Abschnitt 7.
- [ ] **Step 6: Tag und Übergabe**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && git tag design-p10 && git log --oneline origin/test..design-p10 && git rev-parse design-p10`
Expected: Exit 0, Tag gesetzt. Brachte der Rebase in Step 1 neue Commits mit, liefen Gate und Vergleich bereits auf dem rebaseten Stand.
Der Eigentümer bekommt:
- den Bericht des Pixelvergleichs („0 abweichende Ansichten von <Zahl>“) und die Screenshots aus `$W/shots/p10/1440x900` und `$W/shots/p10/390x844` (Auswahl: `login`, `tickets`, `ticket`, `kunde-overview`, `projekte`, `finanzen`, `dlg-rechnung-kunde`, `serverpuls`, ein Alt-Dialog)
- die Bundle-Tabelle aus Abschnitt 6 der Ergebnisdatei
- den Hinweis, dass sichtbar nichts Neues erscheint, der Link `/tickets?woid=N` aus Portal-Mails weiter funktioniert und die offenen Punkte in Abschnitt 7 stehen
- den Hinweis, dass damit alle Pakete ausgeliefert sind und Worktree `dev-design` samt Branch `feature/design-weiss` danach entfernt werden können, wenn er das will
- die Push-Zeile `git -C /home/webklar/apps/ticket.webklar.com/dev-design push origin 'design-p10^{commit}:refs/heads/test'`
- die Rücknahme `git revert --no-commit design-p9..design-p10` (Commit und Push auf `test` durch den Eigentümer, gilt nur solange P10 das jüngste Paket ist)
- [ ] **Step 7: Nach dem Push (erst, wenn der Eigentümer gepusht hat)**
- Im Log von `webklar-ticket-build` steht „Deployment completed successfully“. Dort läuft `npm install` mit dem neuen Lock durch.
- `curl -s https://ticket.webklar.com/ | grep -o 'assets/index-[^"]*\.js'` entspricht `dist/index.html` des Tags.
- `curl -s -o /dev/null -w '%{http_code}' 'https://ticket.webklar.com/tickets?woid=1'` liefert 200 (Seiten-Fallback).
- Ein lesender Sichttest im Chrome des Eigentümers, ohne Schreibaktion: Ein alter Link `/tickets?woid=<N>` mit einer echten Nummer landet auf `/tickets/<N>`. Ticketliste, ein Ticket, das Menü mit dem Serverpuls-Punkt und ein Dialog (öffnen, mit Esc schließen) sehen aus wie vor dem Push, und die Konsole zeigt keinen Fehler.