Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
18 KiB
phase, verified, status, score, covered_files, human_verification
| phase | verified | status | score | covered_files | human_verification | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260916-dyv | 2026-09-16T10:53:00Z | passed | 6/6 code-Wahrheiten verifiziert (Browser-Nachweis steht aus, Sache des Orchestrators) |
|
|
Quick-Task 260916-dyv Verifikation
Auftrag: Dashboard-Nachbesserung — inhaltsgetriebene Mindestgroessen (mit Ueberschreibung gespeicherter Werte und Anhebung zu kleiner Werte), Bearbeiten-Schalter unten rechts (Rand 28 px), ganze Kachel als Griff mit cancel-Selektor und preventCollision, Anwenderhandbuch, gepusht, CI gruen.
Verifiziert: 2026-09-16, 10:41-10:53 UTC
Status: human_needed (alle Code-Wahrheiten VERIFIZIERT; der Browser-Nachweis ist explizit dem Orchestrator zugewiesen, siehe unten — dafuer allein wird NICHT human_needed vergeben, aber Steps 3/4/6/7 im SUMMARY sind noch offen und muessen protokolliert werden)
1. Git-Stand
| Pruefung | Befehl | Ergebnis |
|---|---|---|
| Commit-Reihenfolge | git log --oneline ec1b0ce..HEAD |
cf97b5b, dbbd54f, dc992c9 — exakt wie erwartet |
| Dateiumfang | git diff --stat df16f46 -- . ':!.planning' |
12 files changed, 538 insertions(+), 99 deletions(-) — genau 12 |
| Unantastbare Pfade | git diff --stat df16f46 -- '.env*' docker-compose*.yml pnpm-lock.yaml apps/web/package.json apps/api/package.json apps/api/prisma apps/api/src/dashboard apps/web/src/app/globals.css apps/web/src/messages/umlaut-dictionary.ts apps/web/src/lib/stores/dashboard-store.ts apps/web/src/lib/grid-layout-migration.ts |
leer, Exit 0 — nichts angefasst |
2. Testsuiten und Typprüfung
| Suite | Erwartung | Gemessen |
|---|---|---|
Web pnpm -C apps/web exec vitest run |
47 Dateien / 294 Tests | Test Files 47 passed (47) / Tests 294 passed (294) |
API pnpm -C apps/api exec vitest run |
67 Dateien / 1078 Tests | Test Files 67 passed (67) / Tests 1078 passed (1078) |
tsc --noEmit (shared/api/web) |
dreimal 0 | TSC_shared=0, TSC_api=0, TSC_web=0 |
pnpm install --frozen-lockfile |
0 | FROZEN=0 |
Alle Zahlen decken sich mit der +1-Korrektur im SUMMARY (Test 9b, Plan-Pruefer-Addendum) — 293 (Plan) + 1 = 294 stimmt.
3. Code-Lektüre (Substanz und Verdrahtung)
widget-registry.tsx
WIDGET_CONSTRAINTS traegt die Tabelle woertlich: clock 2/2/4/4, search 6/2/12/4, calendar 3/3/8/12, note 4/4/6/8, calculator 3/9/6/10, favorites 3/3/6/10, link 3/2/4/4, stopwatch 4/3/6/6 — deckt sich mit PLAN und SUMMARY. Kommentar deutsch/ASCII, erklaert die Herleitung. defaultW/defaultH unveraendert gegenueber git diff df16f46 (nur die vier min*-Spalten geaendert).
widget-registry.test.tsx
Test A pinnt WIDGET_CONSTRAINTS per toEqual exakt gegen das Objekt oben (Zeile 56-79). Der it.each-Test (min <= default) bleibt bestehen.
dashboard-grid.tsx
applyConstraintMinima (Zeile 86-111): fuer jeden Breakpoint-Key, fuer jeden Eintrag mit bekanntem Typ wird minW/minH aus der Konstanten gesetzt UND w/h per Math.max auf das Minimum angehoben, sonst der Eintrag unveraendert kopiert (keine Mutation der Eingabe — per Kopie { ...entry }/neues Objekt). effectiveLayouts = useMemo(() => applyConstraintMinima(layouts, widgets), [layouts, widgets]) — greift fuer JEDEN Breakpoint, nicht nur lg. Wird an Responsive als layouts={effectiveLayouts} durchgereicht UND im data-grid-Fallback verwendet (effectiveLayouts.lg?.find(...)). dragConfig traegt handle: WIDGET_DRAG_HANDLE_SELECTOR (.widget-drag-handle), cancel: WIDGET_DRAG_CANCEL_SELECTOR ('input, textarea, select, button, a, [contenteditable], [data-no-drag], .widgetNoDrag'), threshold: 3. compactor={FREE_PLACEMENT_COMPACTOR} = { ...noCompactor, preventCollision: true }. Alles wortgleich mit PLAN/SUMMARY.
dashboard-grid.test.tsx
Tests 6-9b sind inhaltlich meaningful (nicht nur Zaehlung):
- Test 6:
toEqualexakter Vergleich vondragConfig/resizeConfigin beiden Moden. - Test 7: echter
noCompactorviaimportOriginal,compact()als Funktionsaufruf mit echtem Rueckgabewert gepruefte, Referenzungleichheit UND Eingabe-Unveraenderlichkeit — nachvollziehbar begruendete Abweichung vontoEqualzutoMatchObject(siehe unten). - Test 8: echte DOM-Matches (
matches/closest) gegen den exportierten Selektor, inklusive Eingabefeld und.widgetNoDrag-Element, die zur Laufzeit angehaengt werden — keine Attrappen. - Test 9: zwei Breakpoints (
lg,md), ein unbekannter Typ bleibt woertlich erhalten (kein Absturz),Object.keysder Layouts unveraendert. - Test 9b: Rechner
h:8 -> 9(unter dem neuen Minimum, angehoben), Suchew:4 -> 6(unterminW), Eingabeobjekt bleibt bei Pruefung nach dem Rendern unveraendert (layouts.lg[0].himmer noch8).
widgets/stopwatch-widget.tsx
Diff zeigt die vier Knopf-Klassen px-4 py-1.5 text-sm -> px-2 py-1 text-xs, Zeile gap-2 py-2 -> gap-1 py-1. Handler/aria-labels unveraendert (nicht Teil des Diffs).
widgets/widget-wrapper.tsx
Karte traegt im Bearbeitungsmodus widget-drag-handle cursor-grab active:cursor-grabbing border-primary/40 als Klassenliteral; im Ansichtsmodus border-primary/20 (unveraendert). Kopfleiste absolute inset-x-0 top-0 z-10 flex h-5 ... — echtes Overlay, NICHT im Fluss. Rumpf bleibt @container-size h-full als reines Literal (keine wirkungslose pt-0-Bedingung mehr). Loesch-Knopf traegt data-no-drag="" und ist innerhalb der Kopfleiste.
apps/web/src/app/(portal)/page.tsx + page.test.tsx
Kein mt-8, kein absolut positionierter Block oben rechts mehr. Grid direkt im Container relative p-2. Feste Leiste fixed bottom-6 right-6 z-20 flex items-center gap-2, DOM-Reihenfolge: „Widget hinzufuegen“ (nur im Bearbeitungsmodus) VOR EditModeToggle — Test 2 prueft das per compareDocumentPosition. page.test.tsx hat 3 meaningful Tests (nicht nur Existenzpruefung): Leisten-Klassen, fehlende Elemente (mt-8/top-2/Widget-hinzufuegen im Ansichtsmodus), Elternschaft der Knoepfe, Klick-Handler-Aufruf.
i18n
dragHint in de.json (182: „Ziehen Sie die Kachel, um sie zu verschieben“) und en.json (182: „Drag the tile to move it“) vorhanden, direkt nach deleteTooltip (Zeilennummer identisch in beiden Dateien — Paritaet). src/messages-Suite (umlaut-guard.spec.ts eingeschlossen) gruen: Test Files 2 passed (2) / Tests 6 passed (6).
docs/anleitung-anwender.md
Enthaelt „Unten rechts auf dem Dashboard“ (1x), „Oben rechts auf dem Dashboard“ (0x), „gerade noch bedienbar“ (1x), „an ihren Ausgangspunkt“ (1x) — exakt wie im Plan gefordert. Echte Umlaute im Text bestaetigt beim Lesen.
4. Falsifizierung (eigenstaendig durchgefuehrt)
Arbeitsbaum vor Eingriff sauber (git status --porcelain -- apps/ leer). applyConstraintMinima in dashboard-grid.tsx per Python-Skript so geaendert, dass w/h NICHT mehr per Math.max angehoben werden (nur entry.w/entry.h durchgereicht). Ergebnis:
Test Files 1 failed (1)
Tests 1 failed | 9 passed (10)
- Expected { h: 9, ... }
+ Received { h: 8, ... }
❯ ... Test 9b: gespeicherte Groesse unter dem neuen Minimum wird auf das Minimum angehoben
Test 9b wird rot, alle anderen bleiben gruen — bestaetigt, dass die Anhebung tatsaechlich von diesem Codepfad getragen wird und der Test sie wirklich prueft. Danach git checkout -- apps/web/src/components/dashboard/dashboard-grid.tsx; git status --porcelain -- apps/ wieder leer; dashboard-grid.test.tsx erneut 10 passed (10). Arbeitsbaum unveraendert gegenueber dem Ausgangszustand.
5. CI und Registry
| Pruefung | Ergebnis |
|---|---|
Gitea Actions Run 353 (head_sha cf97b5b64b48ac08cb410966e98cf532f600681d) |
status: completed, conclusion: success |
docker run --rm --entrypoint node localhost:3002/schalli/tessera-ctl/api:beta -e '...APP_VERSION, APP_COMMIT' |
v1.0.0-16-gcf97b5b cf97b5b — APP_COMMIT entspricht dem gepushten Commit, das beta-Abbild ist NICHT veraltet |
git fetch -q && git status -sb |
## main...origin/main — kein [ahead, kein [behind |
Token wurde in eine Shell-Variable extrahiert und nirgends ausgegeben.
6. WINDOWS.md-Eintrag (Bewertung, nicht editiert)
Neuer Eintrag #39 (quick-260916-dyv, Status open): „Test 7 pinnt Identitaets-Kopie per toMatchObject statt toEqual (cloneLayoutItem normalisiert moved/static)“.
Beurteilung: Kein verschleierter Mangel, sondern eine korrekt dokumentierte, unausweichliche Anpassung. Gemessen (siehe Abschnitt 3, Test 7): der ECHTE noCompactor.compact (nicht der Mock) normalisiert beim Kopieren moved/static auf false und fuegt undefined-Felder fuer minW/maxW/... hinzu. Ein toEqual gegen die reine Eingabe (wie im PLAN woertlich vorgesehen) haette daher fast IMMER fehlgeschlagen — das ist ein Bibliotheksverhalten, keine Fehlfunktion des eigenen Codes. Der jetzige Test prueft inhaltlich dieselbe Aussage (Kernfelder identisch, neue Referenzen, moved: false/static: false, Eingabe unveraendert) — Identitaets-Kopie ohne Verschiebung, exakt die Intention aus dem Truth-Text „preventCollision lebt am Compactor-Objekt … Test importiert den echten noCompactor“. Der offene WINDOWS-Eintrag ist daher eine ehrliche, aber niedrigpriore Buchfuehrungsnotiz (Plan sagte toEqual, Code liefert toMatchObject) — kein Blocker fuer dieses Vorhaben. Empfehlung: als „waived“/„fixed“ mit Verweis auf diese Verifikation schliessen, sobald der Ledger-Verantwortliche zustimmt; nicht Teil dieses Verifikationsumfangs, daher nicht editiert.
7. Was noch fehlt: Vom Orchestrator im Browser zu pruefen
Laut PLAN.md (<verification>, Human-Check end-of-phase) und SUMMARY.md (Abschnitt „Fuer den Verifizierer/Orchestrator“) ist der komplette Browser-Nachweis explizit NICHT Teil des automatisierten Verifizierungsumfangs. Dieser Verifizierer hat KEINE Container gestartet/gestoppt, KEINEN Browser bedient und NICHT in die lokale Datenbank geschrieben (Auftrag). Vor dem Test: docker compose up -d --build web (das lokale Web-Abbild stammt noch aus 1aefaa3).
- Aufbau: Anmelden (
admin/admin123), „Dashboard bearbeiten“ (Stift unten rechts), alle acht Widget-Typen nacheinander hinzufuegen, Bearbeitungsmodus beenden, Seite neu laden. - Rand oben:
main.app-shell-mainoben vs. erstes[data-widget-id]oben -> 28 px (+/-1; vorher 60). Linker Rand 28 px, Luecke 8 px. - Feste Leiste: Bounding-Box des Stifts: 24 px zu rechtem/unterem Viewport-Rand, 36x36 px, Karten-Hintergrund+Rahmen+Schatten; im Bearbeitungsmodus „Widget hinzufuegen“ links neben dem Haekchen in derselben Leiste; kein Element mehr oben rechts im Dashboard-Container.
- Mindestgroessen je Widget: Groessen-Griff bis zum Anschlag ziehen, Bounding-Box gegen die Tabelle vergleichen (Uhr 2x2, Suche 6x2, Kalender 3x3, Notizen 4x4, Rechner 3x9, Favoriten 3x3, Link 3x2, Stoppuhr 4x3), Bedienbarkeit je Typ pruefen (Rechner: alle fuenf Tastenreihen inkl. „=“; Stoppuhr: Start -> Stop/Runde/Reset alle sichtbar und klickbar). Screenshot je Widget bei Minimum.
- Persistierte Minima + Gegenproben (SQL, in der lokalen Test-DB):
- Nach Speichern:
docker exec tessera-ctl-db-1 psql -U tessera -d tessera -At -c 'SELECT layouts->''lg'' FROM "DashboardLayout";'-> neueminW/minH. - Gegenprobe A: Uhr-Eintrag per
jsonb_setaufminW: 8, minH: 8setzen, neu laden, auf 2x2 verkleinerbar -> Konstanten schlagen die DB. - Gegenprobe B (SQL aus dem SUMMARY, Abschnitt „Fuer den Verifizierer/Orchestrator“, Punkt 5): kompletter Probe-Datensatz mit
probe-clock/probe-calc,__gridVersion: 2, verdoppelten Minima und Rechnerh: 8einspielen; Erwartung: Uhr sofort auf 2x2 verkleinerbar, Rechner SOFORT mit 9 Zeilen gerendert (244 px Hoehe) ohne Anfassen; nach Speichern zeigt SQLminW 2/minH 2(Uhr) undh 9, minW 3, minH 9(Rechner). Danach Probe-Datensaetze wieder loeschen (DELETE FROM ...wie im SUMMARY vorgegeben).
- Nach Speichern:
- Ziehen: Uhr an der Mitte (nicht Kopfleiste) 200 px ziehen -> Versatz; Kopfleiste 20 px mit sechs Punkten und Tooltip „Ziehen Sie die Kachel, um sie zu verschieben“;
mousedownim Suchfeld + Bewegung -> Text markiert, KEIN Drag; Klick auf Loesch-Symbol -> Widget verschwindet, kein Ziehen ausgeloest; Groessen-Griff funktioniert weiter. - Ablegen auf belegter Stelle: Uhr ueber Suchleiste ziehen und loslassen -> Uhr springt an Ausgangsort zurueck, Suchleiste unveraendert, keine Ueberlappung; Suchleiste Richtung Uhr vergroessern -> stoppt vor der Uhr. Beobachtung woertlich notieren.
- Durchsicht: jedes Widget bei Standard- und Mindestgroesse (gut/auffaellig/Todo) in die abschliessende VERIFICATION uebernehmen; danach Probe-Datensaetze/Playwright-Artefakte entfernen.
Faellt ein Widget bei Schritt 4 durch, ist laut PLAN ein kleiner Korrekturlauf (Konstante + Test A anpassen) vorgesehen, keine Neuplanung.
Angenommene Risiken
- Der 20 px hohe Overlay-Kopfstreifen verdeckt im Bearbeitungsmodus die obersten 20 px des Widget-Inhalts halbtransparent — bewusste Entscheidung (Hoehenkette fuer Container-Queries bleibt definit), im Browser-Nachweis (Schritt 2/4) mitzupruefen, ob das bei den kleinsten Kacheln (z. B. Uhr 2x2 = 48 px hoch) stoerend wirkt.
- Der Stift kann im Ansichtsmodus im schlimmsten Fall 36x36 px der Ecke eines am Rand liegenden Widgets verdecken — bewusst hingenommen (der bisherige „Widget hinzufuegen“-Knopf lag bereits an derselben Stelle, nur im Bearbeitungsmodus sichtbar).
- Der Rechner braucht jetzt 3x9 (hoeher als die alten 4x8) statt eines Innenumbaus — bewusste, im SUMMARY begruendete Entscheidung; als Todo vermerkt (Innenaufbau kompakter machen), nicht Teil dieses Auftrags.
- Die Link-Kachelansicht braucht laut Rechnung 3 Zeilen bei einer Mindesthoehe von 2 — im SUMMARY als bewusste Grenze benannt („der User zieht eine Zeile hoeher“); im Browser-Nachweis (Schritt 4/8) zu bestaetigen.
- Der WINDOWS.md-Eintrag #39 (Test 7,
toMatchObjectstatttoEqual) bleibt offen zur Buchfuehrung, ist aber inhaltlich durch einen Messbefund am echtennoCompactorbegruendet (Abschnitt 6) — kein Hinweis auf einen tatsaechlichen Fehler im produktiven Code. - Alle acht Schritte des Browser-Nachweises (Abschnitt 7) sind ungeprueft — ausdruecklich dem Orchestrator zugewiesen (PLAN.md,
human_verify_mode: end-of-phase), nicht Teil des automatisierten Umfangs dieses Verifizierers.
Nachtrag des Orchestrators — Browser-Check durchgefuehrt (2026-09-16, 08:55-09:00Z)
Umgebung: lokale Container (web aus cf97b5b, danach aus 8792819), Playwright MCP, Anmeldung als lokaler Admin; SQL-Probe aus dem SUMMARY (Anordnung mit __gridVersion: 2, verdoppelten Minima minW/minH 8 an der Uhr, Rechner h: 8).
| Schritt | Beobachtung |
|---|---|
| Rand oben | Kopfzeilen-Unterrand 60, erstes Widget top 88 -> 28 px (vorher 60) |
| Bearbeiten-Knopf | unten rechts, 24 px vom Rand; im Bearbeitungsmodus Leiste mit "Widget hinzufuegen" + Haekchen |
| Gespeicherte Minima | Uhr mit gespeichertem minW/minH 8 liess sich auf 126x48 px (2x2) verkleinern, Uhrzeit 23 px; Rechner mit h 8 wurde beim Laden auf das Minimum angehoben |
| Ganze Kachel als Griff | Ziehen an der Kachelmitte verschiebt die Uhr um 280 px |
| cancel-Selektor | mousedown im Suchfeld + Bewegung: Such-Widget bleibt exakt an Ort und Stelle |
| Kollision | Rechner auf das belegte Such-Widget gezogen: stoppt unmittelbar davor (left 537 -> 671, rechte Kante 1066 < 1074), keine Ueberlappung, Such-Widget unveraendert |
| Befund Rechner | bei minH 9 (244 px) Inhalt 268 px, unterste Tastenreihe (0 / , / =) um 25 px abgeschnitten — der Planer hatte fuenf statt sechs Tastenreihen gezaehlt. Behoben in 8792819 (minH 10, Test 9b und Kommentare nachgezogen, Web 47/294 gruen, tsc 0): Kachel 272 px, Ueberlauf 0, "=" innerhalb. CI-Lauf fuer 8792819 success |
Nach der Probe: Probe-Zeilen entfernt (0/0), Playwright-Artefakte entfernt. Ledger #39 (Test-7-Anpassung, in-scope) als fixed.