390 lines
20 KiB
Markdown
390 lines
20 KiB
Markdown
# 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.
|