20 KiB
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_moduleswird aus Git genommen, danach läuft im Worktree ein echtesnpm 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 percreateRequire - 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.jsonnoch die Lock-Datei nochtests/im Repo. - Kein Request darf einen echten Host erreichen. Jeder nicht nachgebildete Request wird abgebrochen und als Verstoß
external-requestgemeldet. - 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.woff2undinter-latin-ext.woff2, gesetzt peraddInitScriptals--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üfungoverflow-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(mussnode_modulesenthalten)
Interfaces:
-
Produces: Im Worktree laufen
npm testundnpm run buildohne 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
grepauf.gitignoreerscheint keine Zeile. -
Danach erscheint eine Zeile
node_modulesodernode_modules/. -
Step 2:
node_modulesaus 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/leadsmit Umleitung insrc/App.jsx, den pausierten Menüpunkt insrc/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
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.shsetztWT,W,TMPDIR(Scratchpad, wenn als$CLAUDE_SCRATCHPADbekannt, sonst$W/tmp),set -o pipefail,cd "$WT".check.sh [--nur-tests|--nur-build]npm testschreibt nach$TMPDIR/test.log. Ausgegeben werden die Zeilen# pass/# fail.- Der Build läuft mit
NODE_OPTIONS=--max-old-space-size=1200untersystemd-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]startetnodeuntersystemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0.build.sh <worktree> <name>- Baut nach
$W/dist/<name>/mitVITE_APPWRITE_ENDPOINT=https://ticket.test/v1,VITE_APPWRITE_PROJECT_ID=testundVITE_PROJECT_ADMIN_URL=https://portal.test. - Prozess-Variablen haben in Vite Vorrang vor
.env.production. - Dazu
--outDirund dieselbe Speichergrenze wiecheck.sh.
- Baut nach
createBackend({ fixtures, scenario })ausbackend.mjs.scenarioist'angemeldet' | 'abgemeldet' | 'mfa' | 'fehler'.- Rückgabe:
{ handle(route, request), calls: [{ method, url, body }], violations: [] }.
applyQueries(documents, queries) → { total, documents }ausappwrite-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-URLVITE_PROJECT_ADMIN_URL-Basen mit Pfad/api/…-Literaledatabases.*-Aufrufe mit Collection-Konstantenaccount.*- undclient.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:
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/xwird abgebrochen und steht als{ kind: 'external-request', url }inviolations. -
GET https://ticket.test/v1/accountliefert 200 im Szenarioangemeldet, 401 inabgemeldetund inmfa(nach dem Login) 401 mittype: 'user_more_factors_required'. -
Im Szenario
fehlerliefertGET …/collections/worksheets/documents500. -
Jedes Muster aus
endpunkte.mdliefert im Szenarioangemeldeteine Antwort, nie eineexternal-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:
applyQueriesimplementieren
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-adminmit 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_STATUSESplusIn Kontakt,Zugesagt,AbgesagtundAssigned. 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 mitsupporters: '[{"userId":"kenso-admin","task":"DNS geprüft"}]' - Arbeitszeit von
kenso-adminüber 90 min mitcountsToPlan: true
- Git-Push ohne Zeit (
- Projekt
WEBklar/physio-am-markt
- 10
websiteProjectsaller 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 mitstatus: '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.shvollstä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.shaus Task 3. -
Produces: Formate von
VIEWSundGATESwie in der Übersicht. Zusätzlich:runChecks(page) → Promise<Array<{ check, selector, detail }>>auschecks.mjs.submitReachable(page)ausviews.mjssucht den Absende-Knopf im offenen Dialog ([type=submit], sonst den letzten Primärknopf), ruftscrollIntoViewauf 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
--onlyendet mit Exit 2. - Einträge in
viewseines Gates dürfen mit*enden (Präfix). --staticlädt ein HTML-Verzeichnis ohne Backend. So prüft man das Werkzeug am Prototyp.
- Eine unbekannte ID in
-
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
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,hasTouchunddeviceScaleFactor: 2. - Dazu
locale: 'de-DE'undtimezoneId: 'Europe/Berlin'. - Ausgabe:
<ausgabe>/<w>x<h>/<id>.pngund<ausgabe>/report.jsonmit{ 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-surfaceundlow-contrastdü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-surfaceundlow-contrast. - Zusätzlich hat
$W/shots/vorher/report.jsonfürticketsmindestens einendark-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.