Files
tickte-system/docs/superpowers/plans/2026-10-05-weisses-design-p0-vorbereitung.md
T

390 lines
20 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“ P0: Vorbereitung – 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 P0 baut und testet der Worktree sauber, und der tote Code ist weg. Ein Screenshot- und Prüfwerkzeug erzeugt für jede Ansicht Bilder und meldet Design-Verstöße. Dabei wird kein Produktionssystem berührt.
**Architecture:**
- `node_modules` wird aus Git genommen, danach läuft im Worktree ein echtes `npm ci`.
- Das Werkzeug liegt außerhalb des Repos unter `$W`.
- Es baut die App mit **Vite** gegen erfundene Test-Hosts (`https://ticket.test`, `https://portal.test`). Vite nutzt intern esbuild und liefert dasselbe CSS-Bündel und dieselbe Reihenfolge wie der Produktions-Build. Ein eigenes esbuild-Bündel müsste Vite-Env und JSX-Einstellungen nachbauen. Die Spec nennt „esbuild-Bündel“, gleichwertig ist dieser Weg.
- Playwright beantwortet alle Anfragen aus Testdaten und blockiert jeden anderen Host.
**Tech Stack:**
- Vite 5
- playwright-core 1.62.1 aus `/root/.npm/_npx/db89d7302a373f10/node_modules/playwright-core`, Laden per `createRequire`
- Browser `/root/.cache/ms-playwright/chromium_headless_shell-1234/chrome-headless-shell-linux64/chrome-headless-shell`
- Node 24
**Spec:** `docs/superpowers/specs/2026-10-04-weisses-design-design.md`, Paket P0 und „Abnahme“. Globale Regeln stehen in `2026-10-05-weisses-design-00-uebersicht.md`. Die Befehlsregeln dort (`env.sh`, `check.sh`, `run-browser.sh`) entstehen in diesem Paket.
## Global Constraints
Es gelten die Regeln der Übersicht. Für P0 kommt hinzu:
- Das Werkzeug ändert weder `package.json` noch die Lock-Datei noch `tests/` im Repo.
- Kein Request darf einen echten Host erreichen. Jeder nicht nachgebildete Request wird abgebrochen und als Verstoß `external-request` gemeldet.
- Für die Bilder blendet das Werkzeug die Schrift Inter ein, damit die Breiten realistisch sind. Quelle: `/home/webklar/apps/webklar.com/dev-relaunch/public/fonts/inter-latin.woff2` und `inter-latin-ext.woff2`, gesetzt per `addInitScript` als `--font`. Am Repo ändert sich dadurch nichts.
## Review Focus
P0 liefert die Testdaten und Prüfungen für die Übersichtspunkte 1 bis 5:
- Altdaten (`altdaten`)
- lange Inhalte (`altdaten`, Prüfung `overflow-x`)
- Teilausfall (Szenario `fehler`)
- Tastatur (Schritte über `steps`)
- viele Einträge (`viel`)
---
### Task 1: Worktree lauffähig machen
**Files:**
- Modify (Git-Index): `node_modules/**` wird aus Git genommen, nicht gelöscht
- Read: `.gitignore` (muss `node_modules` enthalten)
**Interfaces:**
- Produces: Im Worktree laufen `npm test` und `npm run build` ohne Symlink.
- [ ] **Step 1: Startprüfung**
Run: `cd /home/webklar/apps/ticket.webklar.com/dev-design && git fetch origin && git log --oneline origin/test..feature/terminplaner-tp2 -- src/pages/LeadsPage.jsx src/hooks/useLeads.js && git rebase origin/test && git status --short | grep -v '^ D node_modules' ; grep -n '^node_modules' .gitignore`
Expected:
- Die `git log`-Zeile für TP2 gibt nichts aus. Gibt sie etwas aus: stoppen und den Eigentümer fragen.
- Der Rebase läuft ohne Konflikt.
- Vor dem `grep` auf `.gitignore` erscheint keine Zeile.
- Danach erscheint eine Zeile `node_modules` oder `node_modules/`.
- [ ] **Step 2: `node_modules` aus dem Git-Index nehmen**
Run: `git rm -r -q --cached node_modules && git commit -q -m "node_modules nicht mehr im Repo (Deploy installiert ohnehin neu)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>" && git show --stat HEAD | tail -1`
Expected: rund 8890 Löschungen.
- [ ] **Step 3: Sauber installieren**
Run: `rm -rf node_modules && systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0 npm ci --no-audit --no-fund && git status --short`
Expected: Exit 0 und keine Zeile aus `git status`.
- [ ] **Step 4: Ausgangswerte festhalten**
Run: `cd /home/webklar/apps/ticket.webklar.com/dev-design && T=/root/reports/ticketsystem-design-2026-10-04/werkzeug/tmp && mkdir -p "$T" && npm test > "$T/p0-test.log" 2>&1; echo "exit $?"; grep -E "^# (pass|fail)" "$T/p0-test.log"; systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0 env NODE_OPTIONS=--max-old-space-size=1200 npm run build 2>&1 | tail -6`
Task 1 und 2 laufen vor `env.sh` (kommt in Task 3), deshalb der feste Ordner `$T`.
Expected: `exit 0`, `# pass 312`, `# fail 0` und ein Build ohne Fehler. Testzahl und Bundle-Größen (JS, CSS) kommen in die Commit-Nachricht von Task 2.
### Task 2: Toten Code löschen
**Files:**
- Delete: `src/pages/LeadsPage.jsx`, `src/hooks/useLeads.js`
- Keep: `src/lib/leads.js`, die Route `/leads` mit Umleitung in `src/App.jsx`, den pausierten Menüpunkt in `src/lib/navLinks.js`
- [ ] **Step 1: Unbenutzt beweisen**
Run: `cd /home/webklar/apps/ticket.webklar.com/dev-design && rg -n "LeadsPage|useLeads" src tests`
Expected: Treffer nur in den beiden Dateien selbst.
- [ ] **Step 2: Löschen und prüfen**
Run: `cd /home/webklar/apps/ticket.webklar.com/dev-design && T=/root/reports/ticketsystem-design-2026-10-04/werkzeug/tmp && git rm -q src/pages/LeadsPage.jsx src/hooks/useLeads.js && npm test > "$T/p0-test.log" 2>&1 && systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0 env NODE_OPTIONS=--max-old-space-size=1200 npm run build > "$T/p0-build.log" 2>&1 && echo OK`
Expected: `OK`.
- [ ] **Step 3: Commit**
```bash
git commit -q -m "LeadsPage und useLeads entfernt (seit Lead-Pause unbenutzt)" -m "Ausgangswerte: <Testzahl>, JS <x> B, CSS <y> B" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
```
### Task 3: Werkzeug – Umgebung, Build, Test-Backend
**Files** (alle unter `$W=/root/reports/ticketsystem-design-2026-10-04/werkzeug/`):
- Create: `env.sh`, `check.sh`, `run-browser.sh`, `build.sh`
- Create: `backend.mjs`, `appwrite-query.mjs`, `endpunkte.md`, `endpunkte.mjs`
- Create: `fixtures/standard.mjs`, `fixtures/altdaten.mjs`, `fixtures/viel.mjs`
- Create: `README.md` (Bedienung in zehn Zeilen)
- Test: `selftest.mjs`
**Interfaces:**
- Produces:
- **`env.sh`** setzt `WT`, `W`, `TMPDIR` (Scratchpad, wenn als `$CLAUDE_SCRATCHPAD` bekannt, sonst `$W/tmp`), `set -o pipefail`, `cd "$WT"`.
- **`check.sh [--nur-tests|--nur-build]`**
- `npm test` schreibt nach `$TMPDIR/test.log`. Ausgegeben werden die Zeilen `# pass`/`# fail`.
- Der Build läuft mit `NODE_OPTIONS=--max-old-space-size=1200` unter `systemd-run` (1500M) und schreibt nach `$TMPDIR/build.log`. Ausgegeben werden die Größen der `.js`- und `.css`-Dateien.
- Exit ist 0 nur, wenn beide Teile Exit 0 hatten.
- **`run-browser.sh <skript.mjs> [args]`** startet `node` unter `systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0`.
- **`build.sh <worktree> <name>`**
- Baut nach `$W/dist/<name>/` mit `VITE_APPWRITE_ENDPOINT=https://ticket.test/v1`, `VITE_APPWRITE_PROJECT_ID=test` und `VITE_PROJECT_ADMIN_URL=https://portal.test`.
- Prozess-Variablen haben in Vite Vorrang vor `.env.production`.
- Dazu `--outDir` und dieselbe Speichergrenze wie `check.sh`.
- **`createBackend({ fixtures, scenario })`** aus `backend.mjs`.
- `scenario` ist `'angemeldet' | 'abgemeldet' | 'mfa' | 'fehler'`.
- Rückgabe: `{ handle(route, request), calls: [{ method, url, body }], violations: [] }`.
- **`applyQueries(documents, queries) → { total, documents }`** aus `appwrite-query.mjs`.
- [ ] **Step 0: Umgebungs-Skripte anlegen**
`env.sh`, `check.sh`, `run-browser.sh` und `build.sh` werden laut Interfaces geschrieben. `chmod +x` gilt für alle außer `env.sh`.
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && echo "$WT $W $TMPDIR" && "$W/check.sh" --nur-tests; echo "exit $?"`
Expected: drei Pfade, die Zeilen `# pass`/`# fail` und `exit 0`.
- [ ] **Step 1: Endpunkt-Inventar anlegen**
`endpunkte.mjs` sucht in `$WT/src`:
- `fetch(` mit Basis-URL
- `VITE_PROJECT_ADMIN_URL`-Basen mit Pfad
- `/api/…`-Literale
- `databases.*`-Aufrufe mit Collection-Konstanten
- `account.*`- und `client.call`-Aufrufe
Es gibt eine Liste `{ datei, methode, muster }` aus. Daraus entsteht von Hand `endpunkte.md`, eine Tabelle mit diesen Spalten:
- Quelldatei
- Endpunkt-Muster
- Fixture-Schlüssel
- Antwortform (Felder, die der Code liest)
- Verhalten im Szenario `fehler`
`fehler` ist so festgelegt:
| Anfrage | Antwort im Szenario `fehler` |
|---|---|
| `GET …/collections/worksheets/documents` | 500 |
| `GET …/collections/employees/documents` | 500 |
| `GET https://portal.test/api/admin/employees` | 500 |
| `GET https://portal.test/api/team/*` | 500 |
| `GET …/collections/workorders/documents` mit `limit(1)` (Zähler) | 500 |
| alle anderen | normal |
- [ ] **Step 2: Failing Selbsttest schreiben**
`selftest.mjs` prüft mit `node:assert`:
```js
const { total, documents } = applyQueries(docs, ['equal("status", ["Open","Occupied"])', 'notEqual("type", ["Akquise"])', 'orderDesc("$createdAt")', 'limit(2)'])
// total = Treffer vor limit, documents.length === 2, neueste zuerst
applyQueries(docs, ['equal("woid", ["100131"])']) // findet woid als Zahl oder Text
applyQueries(docs, ['offset(2)', 'limit(1)']) // drittes Dokument
applyQueries(docs, ['cursorAfter("id2")', 'limit(1)'])
applyQueries(docs, ['select(["woid","totalTime"])']) // nur diese Felder plus $id
```
Weitere Fälle:
- Ein Request an `https://example.com/x` wird abgebrochen und steht als `{ kind: 'external-request', url }` in `violations`.
- `GET https://ticket.test/v1/account` liefert 200 im Szenario `angemeldet`, 401 in `abgemeldet` und in `mfa` (nach dem Login) 401 mit `type: 'user_more_factors_required'`.
- Im Szenario `fehler` liefert `GET …/collections/worksheets/documents` 500.
- Jedes Muster aus `endpunkte.md` liefert im Szenario `angemeldet` eine Antwort, nie eine `external-request`-Verletzung. Das Muster wird aus der Tabelle gelesen.
- [ ] **Step 3: Selbsttest laufen lassen**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && cd "$W" && node selftest.mjs`
Expected: FAIL (Module fehlen).
- [ ] **Step 4: `applyQueries` implementieren**
Unterstützt werden:
- Filter: `equal`, `notEqual`, `greaterThan`, `lessThan`, `contains`, `search` (Teilstring, ohne Groß-/Kleinschreibung), `isNull`, `isNotNull`
- Sortierung: `orderAsc`, `orderDesc`
- Seiten: `limit` (Standard 25), `offset`, `cursorAfter`
- Felder: `select`
Ein unbekannter Query-Typ ergibt den Verstoß `unknown-query` und wird ignoriert.
- [ ] **Step 5: Backend implementieren**
| Anfrage | Antwort |
|---|---|
| `https://ticket.test/v1/...` | Appwrite-Nachbau |
| `https://ticket.test/api/...` | gleichseitige APIs (Serverpuls u. a.) laut Inventar |
| sonstige `https://ticket.test/...` | Datei aus `$W/dist/<name>/`, unbekannte Pfade bekommen `index.html` |
| `https://portal.test/...` | Portal- und Admin-API laut Inventar. Der Chat-Composer (`loadChatComposer.js`) bekommt eine leere JS- und CSS-Datei, bis P6 den echten Baustein einbindet. |
| andere URLs | `route.abort()` und Verstoß `external-request` |
Schreibende Requests ändern die Testdaten im Speicher. Jeder Request landet in `calls`.
- [ ] **Step 6: Testdaten anlegen**
**`fixtures/standard.mjs`**
- Angemeldet ist `kenso-admin` mit Labels `['admin']`, Name „Kenso Grimm“.
- 4 Mitarbeiter: KNSO, ANDJ, JUSN, NIRE.
- 9 erfundene Kunden aus `quelle/data.js`:
- Stufen fest, lead und archiv
- einer mit `customerStatus: 'lost'` (Abgesagt)
- einer mit `isImportant: true`
- 12 Tickets mit allen Werten aus `TICKET_STATUSES` plus `In Kontakt`, `Zugesagt`, `Abgesagt` und `Assigned`. Arten Webpage, Migration, Project und Akquise.
- **Ticket 100131 ist fest:**
- Kunde „Physiotherapie am Markt“
- `serviceType: 'On Site'`
- Arbeitsblätter:
- Git-Push ohne Zeit (`serviceType: 'GIT'`, `employeeName: 'knso'`, `totalTime: 0`)
- Git-Push mit 45 min (`employeeName: 'ANDJ'`)
- Kommentar (`isComment: true`)
- Arbeitszeit von `andj` über 60 min mit `supporters: '[{"userId":"kenso-admin","task":"DNS geprüft"}]'`
- Arbeitszeit von `kenso-admin` über 90 min mit `countsToPlan: true`
- Projekt `WEBklar/physio-am-markt`
- 10 `websiteProjects` aller Typen, darunter eine Vorlage und ein archiviertes.
- Rechtliches: Dokumente, Versionen und Anbieter.
- Antworten für Finanzen, Serverpuls (mit Zeitreihen für die Diagramme), Gutschriften, Abo-Angebote, E-Mail-Speicher und Chat-Liste, jeweils laut Inventar.
**`fixtures/altdaten.mjs`**
- Ticket `startDate: '2.5.2026'`
- Ticket `deadline: '31.02.2026'`
- Ticket `startDate: '2026-10-07T09:00:00.000Z'`
- Ticket ohne `woid`
- Ticket mit `status: ''`, Ticket mit `status: 'Completed'`
- Ticket ohne `customerName`
- Betreff mit 120 Zeichen
- Kundenname mit 60 Zeichen
**`fixtures/viel.mjs`**
- Ticket 100131 mit 300 Arbeitsblättern
- 250 Tickets, deren Betreff „Test“ enthält
- [ ] **Step 7: Selbsttest grün**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && cd "$W" && node selftest.mjs`
Expected: `selftest ok`.
- [ ] **Step 8: Probebau**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" vorher && ls "$W/dist/vorher/index.html" && rg -n "https://(ticket|project)\.webklar\.com" "$W"/dist/vorher/assets/*.js | cut -c1-160`
Expected: `index.html` ist vorhanden. Treffer der echten Hosts sind nur Texte oder Links für Menschen, keine API-Basen. Jeden Treffer prüfst du einzeln.
- [ ] **Step 9: `check.sh` vollständig prüfen**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh"; echo "exit $?"`
Expected: Die Zeilen `# pass 312` und `# fail 0`, die Bundle-Größen und `exit 0`.
### Task 4: Werkzeug – Ansichten, Prüfungen, Vorher-Bilder
**Files** (unter `$W`):
- Create: `views.mjs`, `checks.mjs`, `gates.mjs`, `shots.mjs`, `shots.sh`
**Interfaces:**
- Consumes: Backend, Testdaten und `build.sh` aus Task 3.
- Produces: Formate von `VIEWS` und `GATES` wie in der Übersicht. Zusätzlich:
- `runChecks(page) → Promise<Array<{ check, selector, detail }>>` aus `checks.mjs`.
- `submitReachable(page)` aus `views.mjs` sucht den Absende-Knopf im offenen Dialog (`[type=submit]`, sonst den letzten Primärknopf), ruft `scrollIntoView` auf und wirft, wenn er danach nicht vollständig im Viewport liegt.
- `shots.sh <name> <ausgabe> [--only ids] [--gate p<N>] [--static <ordner>]`
- Eine unbekannte ID in `--only` endet mit Exit 2.
- Einträge in `views` eines Gates dürfen mit `*` enden (Präfix).
- `--static` lädt ein HTML-Verzeichnis ohne Backend. So prüft man das Werkzeug am Prototyp.
- [ ] **Step 1: Ansichten anlegen**
| ID | Pfad / Schritte |
|---|---|
| `login` | `/login`, Szenario `abgemeldet` |
| `login-code` | Szenario `mfa`: Formular absenden, warten auf `#mfa-code` |
| `login-code-senden` | Szenario `mfa`: Formular absenden, Seite neu laden, warten auf den Knopf „Code senden“ |
| `tickets` | `/tickets` |
| `tickets-suche` | Testdaten `viel`, `/tickets`, Suche „Test“ (heute über „Anwenden“) |
| `ticket` | `/tickets?woid=100131` (heute aufgeklappte Karte) |
| `ticket-arbeitsblaetter` | wie `ticket`, dann Reiter „Arbeitsblätter“ |
| `ticket-altdaten` | Testdaten `altdaten`, `/tickets` |
| `fehler-tickets` | Szenario `fehler`, `/tickets` |
| `dlg-ticket-neu` | Dialog „Neues Ticket“ |
| `dlg-arbeitsblatt` | Dialog „Arbeitsblatt hinzufügen“ |
| `dlg-zeiten` | Dialog „Zeit nachtragen“ |
| `dlg-unterstuetzung` | Dialog „Ich habe unterstützt“ |
| `planboard` | `/planboard` |
| `kunden` | `/customers` |
| `kunde-<reiter>` | `/customers/<id>?tab=<reiter>` mit den Reiter-IDs aus der Übersicht |
| `chat` | `/customers/chat` |
| `gutschriften` | `/customers/gutschriften` |
| `angebote` | `/customers/angebote` |
| `speicher` | `/customers/email-speicher` |
| `projekte` | `/projects` |
| `dlg-projekt-neu` | Dialog „Neues Projekt“ |
| `dlg-projekt-bearbeiten` | Dialog „Projekt bearbeiten“ |
| `dlg-projekt-art` | Art-Frage in `TicketProjects` |
| `zuordnen` | `/projects/zuordnen/<id>` |
| `services` | `/services` |
| `analyse-zustimmung` | Zustimmungsablauf in `AnalyseRow` |
| `finanzen` | `/finance` |
| `dlg-rechnung` | Rechnungsdialog |
| `dlg-zuordnen-zahlung` | `AssignDialog` |
| `finanzen-einstellungen` | `/finance/einstellungen` |
| `rechtliches` | `/rechtliches` |
| `rechtliches-anbieter` | `/rechtliches`, zweiter Reiter |
| `dlg-vertrag` | Vertrags-Formular |
| `dlg-version` | Versions-Formular |
| `admin` | `/admin` |
| `admin-neu` | Mitarbeiter-Formular offen |
| `serverpuls` | `/serverpuls`, warten bis `canvas` gezeichnet ist |
Alle `dlg-*`-Ansichten:
- haben `viewports: [[1440, 900], [390, 844], [1440, 700], [390, 700]]`
- rufen am Ende `submitReachable(page)` auf
Jede Ansicht wartet auf `document.fonts.ready` und 300 ms. Dazu kommt `reducedMotion: 'reduce'`.
- [ ] **Step 2: Prüfungen schreiben**
`checks.mjs` läuft im Browser über alle sichtbaren Elemente:
| Prüfung | Regel |
|---|---|
| `dark-surface` | Verstoß bei eigenem, deckendem Hintergrund mit relativer Helligkeit < 0,5. Für `background-image` mit `linear-gradient` zählt der Mittelwert der Farbstopps. Ausnahmen: `.btn-primary`, `.badge`, `.toast`, `.nav-dot`, `.day.today .dn`, `.sw`, `.code-input`, `[class*="lightbox"]`, `[class*="camera"]` und `canvas`. |
| `low-contrast` | Verstoß bei Textkontrast < 3:1 gegen den nächsten deckenden Hintergrund. Gradienten zählen wie oben. Ausnahmen: `disabled`, `aria-disabled` und Platzhalter. |
| `icon-svg` | Verstoß bei einem sichtbaren `svg` außerhalb von `.brand`, `.login-box > svg` und `.woms-chat .chat-composer *` |
| `overflow-x` | Verstoß bei `scrollWidth > innerWidth + 1` |
| `console-error` | Verstoß bei `pageerror` oder `console.error`. Im Szenario `fehler` sind die erwarteten 500-Meldungen ausgenommen. |
| `external-request`, `unknown-query` | Verstöße aus dem Backend |
| `step-failed` | eine Ausnahme in `steps` |
- [ ] **Step 3: Gates festlegen**
```js
export const GATES = {
p0: { checks: ['external-request', 'console-error', 'step-failed'], views: '*' },
p1: { checks: ['external-request', 'console-error', 'step-failed', 'dark-surface', 'low-contrast', 'overflow-x'], views: '*' },
}
```
Spätere Pakete ergänzen nach dem Schema der Übersicht.
- [ ] **Step 4: Läufer schreiben**
`shots.mjs`:
- Je Ansicht und Viewport ein Kontext. Unter 1000 px Breite gilt `isMobile`, `hasTouch` und `deviceScaleFactor: 2`.
- Dazu `locale: 'de-DE'` und `timezoneId: 'Europe/Berlin'`.
- Ausgabe: `<ausgabe>/<w>x<h>/<id>.png` und `<ausgabe>/report.json` mit `{ view, viewport, issues[] }`.
- Der Exit-Code richtet sich nach `--gate`.
`shots.sh` startet `shots.mjs` über `run-browser.sh`.
- [ ] **Step 5: Vorher-Bilder**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/shots.sh" vorher "$W/shots/vorher" --gate p0`
Expected:
- Exit 0.
- Für jede Ansicht ein Bild je Viewport.
- Kein `external-request`.
- `dark-surface` und `low-contrast` dürfen massenhaft auftreten. Sie sind nicht gate-relevant.
- [ ] **Step 6: Gegenprobe am Prototyp**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/shots.sh" prototyp "$W/shots/prototyp" --static /root/reports/ticketsystem-design-2026-10-04/quelle --only tickets`
Expected:
- 0 Verstöße für `dark-surface` und `low-contrast`.
- Zusätzlich hat `$W/shots/vorher/report.json` für `tickets` mindestens einen `dark-surface`-Treffer. Das beweist, dass die Prüfung anschlägt.
### Task 5: Abschluss P0
- [ ] **Step 1: Tag**
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git rebase origin/test && "$W/check.sh" && git tag design-p0`
Expected: Exit 0 und das Tag ist gesetzt. P0 wird nicht gepusht, sondern geht mit P1 live.
- [ ] **Step 2: Code-Review (sonnet)**
Geprüft werden `603d08c..design-p0` und das Werkzeug (`$W`), mit Blick auf zwei Fragen:
- Kann ein Request einen echten Host erreichen?
- Melden die Prüfungen falsch-negativ?
Wichtige Befunde werden behoben. Ein späterer Commit bekommt das Tag `design-p0-fix1`.
- [ ] **Step 3: Bericht an den Eigentümer**
Der Bericht nennt die Ausgangswerte, den Ort der Vorher-Bilder und den Hinweis, dass P0 unsichtbar ist und mit P1 live geht.