Prüfstand: entry.jsx zeigt nur noch die echte Seite WorkersPage (BrowserRouter, Adresse mit ?tab= lesbar); shot.cjs neu mit den Szenarien A–M
(Hochhaus, Regie, Handy, weniger Bewegung, Notbremse, Ausfall, Detail, Etagen, Pause, Pult P3, Pult P4, Keller, Handy Pult/Keller),
harte Prüfungen und Bildern mit Szenario-Vorsilbe; Befunde an der App stehen getrennt von den Verstößen. Skript-API: Gründe der Bot-Leads
wörtlich wie im Dienst. Kopfzeile am Handy: zwei Reihen (Geld + Pause/Zahnrad, dann die fünf Kennzahlen), schmaler Panel-Rand, 40-px-Knöpfe,
Querformat unter 500 px Höhe in einer Reihe; hustlerKopf.test um die Zusicherungen dazu ergänzt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- gelöscht: components/workers/engine/* (10 Dateien), WorkersStage, WorkersBoard, WorkerCard, WorkerList, WorkersKpis,
die neun Engine-Tests samt workersSmoke (211 Tests) und die Helfer engineVm.mjs und fakeCtx.mjs (nur die Engine-Tests brauchten sie)
- workers.css (310 -> 60 Zeilen) behält nur Einstellungen-Dialog und Interessenten-Panel; @font-face für Pixelify Sans steht jetzt in hustler/schrift.css
- workersComponents: kein innerHTML und kein Kanvas rekursiv unter components/workers/**, jeder Knopf mit type, Links nur aus geprüften Feldern
(externe mit noopener), keine Anmeldung in den Bildschirmen, alte Dateien weg und nirgends mehr importiert
- workersCss: jede wk-Klasse im Code hat eine Regel, keine Regel ohne Nutzer; workersFont: Pixelify Sans genau einmal in schrift.css
- hustlerAssets: Pixelify Sans in schrift.css erlaubt; hustlerDialog: Link-Ziele nur aus safeLink, Knöpfe mit type; hustlerSeite: Laden/Fehler/bereit am selben Einhängepunkt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
HochhausWelt.jsx baut die Welt 1:1 aus dem Markup der Zeichenfläche (Himmel, Bühne 2400x1200, Haus-, Licht- und Fensterebenen, Neon, Aufzug,
Raumschilder, Bildschirme, Radar, Schilder, Etagen, 19 Hustler, Schnee); Figuren, Zusätze, graue Raum-Ebenen samt Papier-Zettel, Briefkasten-Zähler
und Etagen-Links folgen dem Dienst-Zustand. useKamera.js ist der Hook für Rad, Ziehen, Pinch, Doppeltipp, Tasten und Gleiten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- scripts/hustler-import.mjs liest pixelwelt-v2 (+ pixelwelt-woms/haus) und schreibt src/assets/hustler, src/styles/hustler/{hochhaus,keller,pult}.css und src/lib/hustlerWelt.js; idempotent
- haus.png mit allen Räumen hell, acht graue Ebenen für die Such-Räume (GRAUE_RAEUME, .hk-raum-grau-<raum>)
- Press Start 2P lateinisch aus @fontsource/press-start-2p@5.2.7 (OFL), noch nirgends importiert
- 40 neue Tests (Assets, Geometrie, CSS-Umformung)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
N5: Der Fokus geht nach dem Blättern über restoreFocus (workersFocus.js) mit preventScroll an die Seitenzeile zurück, aber nur, wenn er
wirklich verloren ging (body, nirgends, alter Knopf ausgehängt). Hat der Nutzer inzwischen Suchfeld oder Raumfilter angesteuert, bleibt er dort.
N6: Scheitert eine Anfrage, steht unter der Meldung „Erneut versuchen“; der Knopf stellt genau diese Anfrage (Raum, Suche, Seite oder Eintrag)
noch einmal und die Seitenzeile bekommt danach den Fokus zurück. CSS: .wk-bl-retry, am Handy 40 px.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Endprüfung P6 (C5, W1, W2):
- Blätterknöpfe 36 px -> 40 px und „Alle anzeigen“ der Einzelansicht (32 px) -> 40 px über
`.wk-bl-pager .btn` und `.wk-bl-single .btn` im Handy-Block; min-height schlägt height: 32px,
keine globale .btn-Regel, kein !important.
- Gekürzte Zeilen: --text-2 statt --text-3 (Kontrast von etwa 2,9:1 auf mehr als 5:1).
- `.wk-bl .wk-bl-table td .cell-sub { overflow-wrap: break-word }` (0,3,1) schlägt das globale
`.table td .cell-sub { overflow-wrap: anywhere }` (0,2,1) („Kaiserslau-tern“ in der Einzelansicht).
- Tests: workersCss.test.mjs (Farbe, Kontrast aus den Token von global.css, Höhen, Spezifität gegen die
globale Regel); Prüfstand botleads und handy-botleads: 81 Prüfungen grün.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Endprüfung P6 (C5, W1): Beim Blättern hängt die Seitenzeile beim Laden aus, und „Alle anzeigen“
wird durch den Raumfilter ersetzt; der Fokus fiel jeweils auf <body>.
- Blättern: gemerkt wird der gedrückte Knopf; nach dem Laden bekommt er den Fokus zurück, ist er
gesperrt (erste oder letzte Seite), der andere. Raum oder Suche bedient der Nutzer inzwischen,
geschieht nichts; scheitert die Anfrage, gibt es keine Seitenzeile und nichts geschieht.
- „Alle anzeigen“: der Raumfilter bekommt den Fokus, sobald die Seite bot aus der Adresse
genommen hat. Nicht beim Einhängen (am Handy öffnete das die Auswahl) und nicht bei einem
anderen Weg zur Liste (Zurück-Taste).
- aria-current nur mit bot (single) und passender Kennung; Zeilen ohne Kennung teilen sich keinen key mehr.
- Tests: botleadList.test.mjs (Fokus-Attrappe über __fakeRef, Mutationsprobe für die Reset-Zeilen),
botleadsView.test.mjs (Quelltext-Probe); der Prüfstand prüft zusätzlich den Fokus nach „Alle anzeigen“.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Skript-API: botleads({ room, q, page, id }) antwortet wie GET /botleads des Dienstes
(items, total, pageSize 50; Telefon nie bei Heilberufen, gekürzte Stubs nur mit id).
60 sichtbare Einträge in vier Räumen (zwei Seiten), 3 gekürzte Stubs, Heilberufe mit
Altbestand-Telefon, eine http-Quelle, zwei feindliche Quellen, lange Namen und Gründe.
Schalter: faults.botleads, botleadsDelayMs, botleadsEmpty (?botleads=leer), botleadCalls.
Fetch-Weiche und entry.jsx (?bot=<Id>) um die Route und den Deep-Link ergänzt.
Szenarien: botleads (Reiter, Liste, Raumfilter, Blättern mit Fokus, Suche, Leerzustände,
Fehlertexte, Deep-Link, Quellen-Links, Telefon) und handy-botleads (390 x 844: kein
seitliches Scrollen, Knopfhöhe, Kontrast der gekürzten Zeile, Wortumbruch).
Der Pin in workersHarness.test.mjs prüft nun die neue Antwortform statt der leeren Liste.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die zeichengleiche Inline-Kopie (FAKE_REACT und Plug-in) ist entfernt; der Baustein ist jetzt die einzige Fassung für workersPultUi.test.mjs und botleadList.test.mjs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Der Commit 1536a3b hatte den Test „reply, approve-preview und start-build warten länger als die übrigen
Pult-Aktionen“ gelöscht. Der neue Test prüft gegen den heutigen Code (ACTION_TIMEOUT_MS 65 s,
REQUEST_TIMEOUT_MS 20 s, Optionen timeoutMs / actionTimeoutMs) mit den Uhren von node:test statt echter
Wartezeit: keine Abhängigkeit von der Last, Grenzen auf die Millisekunde (eine Millisekunde vor der Frist
läuft der Abruf, mit ihr endet er). Die drei langsamen Aktionen nehmen eine Antwort zwischen kurzer und langer
Frist an, claim/sent/discard/done/paste und das Lesen enden mit der kurzen Frist (DOWN_TEXT, status 0, Signal
abgebrochen), ein hängender Abruf endet nach der langen Frist. Derselbe Ablauf mit Standard- und mit
eingespeisten Fristen; ein zweiter Test pinnt, dass auch der Wiederholungsversuch nach einer 401 die Frist
der Aktion behält.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ohne die Option springt die Seite beim Zurückgeben des Fokus zum Ziel: bei einer Zeile weit unten in
einer langen Liste zur Meldung oben, und die Person verliert ihre Stelle. restoreFocus ruft jetzt
target.focus({ preventScroll: true }); der Fokus (und damit die Ansage) wandert, die Ansicht bleibt stehen.
Die Attrappe in workersFocus.test.mjs merkt sich die Argumente, ein neuer Test pinnt genau einen Aufruf
mit dieser Option.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die gemeinsame Regel `.dialog-foot .btn` (32 px) gilt für rund 22 Dialoge der Seite und bleibt unberührt.
Das Panel gibt seinem Knopf die Klasse wk-prospect-close; im Handy-Block von workers.css hebt
`.wk-prospect-close.btn[type="button"]` (Spezifität 0,3,0 gegen 0,2,0, beginnt mit einer wk-Klasse wie
workersCss.test.mjs es verlangt) ihn auf volle Breite und 40 px. Der Desktop behält 32 px.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Jede Aktion sperrt die Knöpfe der Zeile; Chromium nimmt einem gesperrten Knopf den Fokus und gibt ihn nicht
zurück, Tastatur- und Screenreader-Nutzer standen danach am Seitenanfang. begin() merkt sich das fokussierte
Bedienelement und das offene Feld; ist die Anfrage vorbei, geht der Fokus zurück an den Knopf, sonst ans
Textfeld, an den Knopf, der das Feld öffnete, sonst an die Zeile (tabIndex -1). Verlässt die Zeile die
Liste, nimmt die Meldung oben (tabIndex -1) den Fokus, auch bei gleichem Text zweimal nacheinander. Es wird
nur eingegriffen, wenn der Fokus wirklich verloren ging und vor der Aktion ein Bedienelement ihn hatte
(Safari fokussiert Knöpfe nicht). Neues Modul workersFocus.js (rein, document wird übergeben).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
„Als Mensch antworten“ und „Antwort des Kunden einfügen“ ließen sich nur durch erneuten Klick auf den
Auslöser schließen. Esc (aus dem ganzen Feld, mit preventDefault für die Handy-Leiste) und der neue Knopf
„Abbrechen“ schließen das Feld, der Text bleibt in der Liste erhalten, der Fokus geht an den Auslöser
zurück (data-opens am Knopf, gesucht in der Aktionsgruppe: Button reicht kein ref durch). Während einer
Anfrage sind beide gesperrt. Neue Gruppe .wk-pult-panel-btns, auf dem Handy mit dem Hauptknopf oben.
Test-Läufer: ref-Objekte an Tags und eine document-Attrappe für den Fokus.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Bei einer http- oder fremden Adresse zeigte die Zeile „Keine gültige Vorschau-Adresse hinterlegt.“ statt
des Links, ließ aber das Freigeben zu: der Mensch gab eine Vorschau frei, die er nicht ansehen kann. Der
Knopf ist jetzt gesperrt (auch approve() prüft previewHref), der Hinweis nennt Grund und Ausweg („Entwurf
prüfen oder verwerfen“), Verwerfen bleibt frei. Das Zeilenmodell (canApprovePreview) bleibt unverändert.
Das Sperren schließt fail-closed: weichen baseUrl des Dienstes und PREVIEW_HOST in WOMS einmal ab, ist
Freigeben gesperrt, bis WOMS angepasst ist. Prüfstand prüft den gesperrten Knopf.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Der Knopf startete einen Opus-Lauf mit einem einzigen Klick (Handy: voll breit zwischen anderen Knöpfen),
ohne dass sich der Bau in der Oberfläche zurücknehmen lässt. Wie Freigeben und Verwerfen fragt er jetzt
zuerst nach („Wirklich bauen?“, 4 s) und zeigt dabei, dass ein Opus-Lauf startet und die Vorschau gebaut
wird, als hätte der Interessent Ja gesagt (ohne „nur wenn Ja“: der Knopf gilt gerade für unklare Antworten).
rowModel blendet den Knopf bei gesperrtem, verlorenem oder abgeschlossenem Interessenten (gesperrt,
verloren, deal) aus; ohne Kurzfassung entscheidet weiter der Dienst. Prüfstand: die Fehler- und Fokus-Läufe
klicken zweimal, Szenario bauen prüft Frage, Hinweis und Verfall. Doppeltipp-Schutz und Dienst-Härtung
(bauLauf nur nach Ja) bleiben offen, siehe final-fix-report-woms.md.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die Unsicherheits-Meldung (D14) galt nur für status 0. Eine Fehlerseite des Proxys (502, 504) zeigte
„Der Hustler-Dienst ist gerade nicht erreichbar.“, obwohl der Dienst weiterarbeitet und die Mail schon
hinausgegangen sein kann. workersApi.js exportiert UNCERTAIN_STATUS = [0, 502, 504]; actionErrorText
prüft ihn (503, 429, 4xx und 500 bleiben unverändert, ebenso ein Satz des Dienstes unter 502).
ACTION_TIMEOUT_MS steigt auf 65 s, damit der Client nicht gleichauf mit dem proxy_read_timeout (60 s)
läuft und ein 504 zuerst ankommt. Tests über createWorkersApi mit Fehlerseiten für alle acht Aktionen.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die Skript-API kennt jetzt reply, approve-preview, start-build und paste mit den Prüfreihenfolgen und Sätzen des
Dienstes (pultMail.js, paste.js) und einen eigenen Datensatz (?p4=1: Mail-Antwort, zwei Vorschau-Freigaben, unsicher,
Kollegen-Eintrag, zwei gesendete Plattform-Antworten desselben Interessenten). Schalter für 400/409/413, Verbindungsabbruch
(status 0) und eine Wartezeit je Aktion.
Sechs neue Szenarien in shot.cjs: antworten, vorschau, einfuegen, bauen, fokus, handy-p4 (109 Prüfpunkte, Bilder je Zustand).
Drei veraltete P3-Prüfpunkte angepasst (seit P4 bietet eine Plattform-Zeile auch „Antwort des Kunden einfügen“). Bricht ein
Szenario ab, hält abbruch-<Szenario>.png den Zustand fest.
Der Lauf deckt Fehler der App auf (Fokus nach Aktionen, kein Esc/Abbrechen an den Feldern, 32-px-Schließen am Handy); die
Prüfpunkte dazu bleiben rot, an der App ist nichts geändert.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
FAKE_REACT (usePultFeed) und FAKE_UI (PultList) waren dieselben Hooks
doppelt, STUBS und UI_STUBS dieselben Attrappen doppelt. Jetzt gibt es
einen Läufer (FAKE_REACT, mehrere Bauteile mit Zustand, Effekten und
Abbau) und eine Tabelle (STUBS); usePultFeed läuft in einem Bauteil
(mountHook), die gerenderten und die bedienten PultList-Tests teilen
sich die Attrappen über globalThis.__pultFeedFor. Nur Test-Code, das
Verhalten der Tests bleibt gleich (24 Tests grün).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
"Details", "Anzeige öffnen", das Entwurfsfeld und die Aktionsgruppe tragen den Titel der Zeile im
zugänglichen Namen ("Details zu <Titel>"), das sichtbare Wort steht vorn (WCAG 2.5.3); ohne
Titel gilt der schlichte Name. Der Prüfstand sucht die Knöpfe unter den neuen Namen und prüft
zusätzlich die Eindeutigkeit der Namen (W8) und die Abschlusszeile im Reiter Erledigt (W5).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Abgeschlossene Zeilen tragen jetzt ein Badge (Gesendet grün, Verworfen/Erledigt neutral) und die
Zeile "Gesendet von Anna · vor 2 Std." aus doneBy/doneAt statt des Alters ab Anlage. Menschen
(mensch:<id>) werden über die Mitarbeiterliste aufgelöst, ein Worker wie max über das
Rollen-Register als "Max (automatisch)" und nie als "Kollege". Offene Zeilen bleiben unverändert.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>