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

20 KiB
Raw Blame History

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
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:

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
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.