Files
schalli 5a444ec8f2
Tessera CI/CD / Lint & Type Check (push) Successful in 47s
Tessera CI/CD / Tests (push) Successful in 1m5s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 5m21s
Tessera CI/CD / Build & Publish Images (push) Successful in 4m20s
docs(quick-260917-jdf/jdh/jdd/jn2): Aktenstand — Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse
Vier Quick-Tasks mit Plan, Bericht und Verifikation; Browser-Nachweis der
Web-Teile lokal erbracht (Kacheln #284a7b bei #0057b8, Proxy-Symbol trotz
Zertifikatsfehler, Direktbild bei interner Adresse, Sortierung ueber Reload).
Offen: CI-Beweis des Desktop-Skips nach diesem Push, Windows-VM-Probe der
Client-Aenderungen mit dem CI-Paket.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 15:10:20 +02:00

16 KiB

phase, verified, status, score, covered_files, covered_digest, behavior_unverified, overrides_applied, human_verification
phase verified status score covered_files covered_digest behavior_unverified overrides_applied human_verification
quick-260917-jdd 2026-09-17T14:50:00Z human_needed 15/15 must-have truths verified (automated); 4 Browser-Nachweise offen (per Plan an Orchestrator delegiert)
.planning/quick/260917-jdd-favoriten-widget-favicon-ersatzweg-bei-u/260917-jdd-PLAN.md
.planning/quick/260917-jdd-favoriten-widget-favicon-ersatzweg-bei-u/260917-jdd-SUMMARY.md
CHANGELOG.md
apps/api/package.json
apps/api/src/favorites/dto/reorder-favorites.dto.ts
apps/api/src/favorites/favorites.controller.ts
apps/api/src/favorites/favorites.service.spec.ts
apps/api/src/favorites/favorites.service.ts
apps/api/src/favorites/icon-discovery.service.spec.ts
apps/api/src/favorites/icon-discovery.service.ts
apps/api/src/prisma/prisma-tenant.extension.ts
apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx
apps/web/src/components/dashboard/widgets/favorites-widget.tsx
apps/web/src/lib/favorites-api.ts
apps/web/src/messages/de.json
apps/web/src/messages/en.json
docs/anleitung-anwender.md
docs/mandantentrennung-zugriffsklassifikation.md
pnpm-lock.yaml
v1:sha256:64ca3a0bcddb034d35168dbbe8ea5e4ede2db2a02a4ac7f946aa8340e3094dfa 0 0
test expected why_human
Favorit auf einen Host mit Zertifikatsfehler anlegen (z. B. https://self-signed.badssl.com/) und im Widget pruefen, dass das Symbol ueber den Server-Proxy erscheint (data-testid icon-proxy-*, NICHT icon-direct-*). Symbol erscheint ueber den Proxy — der Server toleriert jetzt Zertifikatsfehler (LENIENT_TLS_AGENT). Echter Netzzugriff auf einen TLS-fehlerhaften Host ist in dieser Umgebung nicht verfuegbar; nur per Unit-Test mit gemocktem undici geprueft.
test expected why_human
Favorit auf eine interne Adresse im Firmennetz anlegen (die der SSRF-Schutz des Servers ablehnt) und pruefen, dass das Symbol ueber das Direktbild aus dem Browser erscheint (data-testid icon-direct-*). Proxy antwortet 502, Widget faellt automatisch auf das Direktbild um; bei fehlendem Zertifikatsvertrauen faellt es weiter auf den Buchstaben zurueck. Erfordert echten Zugriff auf ein internes Firmennetz-Ziel und einen echten Browser; lokal nur die onError-Kette per jsdom/Unit-Test geprueft.
test expected why_human
Nach einem Klick auf „Nach oben“/„Nach unten“ die Seite neu laden und pruefen, dass die neue Reihenfolge erhalten bleibt. Reihenfolge ist nach Reload identisch zur vor dem Reload gesetzten Reihenfolge (Server-persistiert via PUT /favorites/order). Erfordert einen laufenden Server + Browser-Reload; die Persistenz ist nur bis zur Service-Ebene per Unit-Test (kein echter DB-Zugriff) geprueft.
test expected why_human
Altbestand mit position=0 (vor diesem Plan angelegte Favoriten) im echten System beim ersten Sortierklick beobachten. Normalisierung zu 0..n-1 ohne Datenverlust oder Fehlermeldung. Erfordert echte Datenbankzeilen mit dem alten Zustand (position=0 fuer mehrere Zeilen); im Unit-Test simuliert (Altbestand-Fixture), aber nicht gegen echte Postgres-RLS geprueft.

Quick Task 260917-jdd: Favoriten-Widget — Symbol-Ersatzweg, Sortierung Verification Report

Task-Ziel: Symbol-Ersatzweg bei Zertifikatsfehlern/internen Adressen (Server-Dispatcher + Browser-Ersatzweg, SSRF-Schutz unangetastet) und manuelle Sortierung per Pfeilen (transaktional, Existenzorakel-Vermeidung). Verified: 2026-09-17 Status: human_needed (alle automatisierten Pruefungen bestanden; vier Browser-Nachweise sind laut Plan explizit an den Orchestrator delegiert und lokal nicht pruefbar)

Commits geprueft

Alle drei im SUMMARY genannten Commits existieren im Git-Verlauf und enthalten genau die zugesagten Dateien:

Commit Zweck Dateien lt. git show --stat
2a562d0 feat(api): Dispatcher + PUT /favorites/order + reorder() package.json, dto/reorder-favorites.dto.ts, favorites.controller.ts, favorites.service.(spec.)ts, icon-discovery.service.(spec.)ts, pnpm-lock.yaml — stimmt mit files_modified des Plans ueberein
b18ac25 feat(web): FavoriteIcon, Sortierpfeile, i18n favorites-widget.(test.)tsx, favorites-api.ts, de.json, en.json — stimmt ueberein
b023d6f docs: CHANGELOG, Handbuch, Nachtraege CHANGELOG.md, prisma-tenant.extension.ts, anleitung-anwender.md, mandantentrennung-zugriffsklassifikation.md — stimmt ueberein

Kein git push, kein Docker-Build, apps/api/prisma/schema.prisma unveraendert seit 54121c1 (weit vor diesem Task) bestaetigt via git diff --quiet 38c1400 -- apps/api/prisma/schema.prisma.

Goal Achievement

Observable Truths (Teil A — Symbol-Ersatzweg)

# Truth Status Evidence
1 icon-discovery.service.ts importiert Agent, fetch as undiciFetch, Response as UndiciResponse; LENIENT_TLS_AGENT-Singleton; fetchWithRedirectGuard ruft AUSSCHLIESSLICH undiciFetch(...) mit dispatcher, redirect: 'manual', signal, headers; kein globaler fetch( mehr; SSRF-Schutz (isPublicHttpUrl je Hop, MAX_REDIRECTS=2, Timeouts 4000ms, MAX_HTML_CHARS=200000, MAX_ICON_BYTES=1MB, image/-Pruefung) unveraendert ✓ VERIFIED Datei vollstaendig gelesen (Z. 1-405); Negativ-Grep auf kommentarbereinigtes fetch( liefert NO_GLOBAL_FETCH_CALL_FOUND; git show 2a562d0 -- icon-discovery.service.ts zeigt einen minimalen, praezise scoped Diff (nur Import, Konstante, ein fetch→undiciFetch-Aufruf plus dispatcher); alle SSRF-Konstanten/-Funktionen (isPublicHttpUrl, Schleife mit MAX_REDIRECTS, Timeouts, Groessendeckel) byteweise unveraendert
2 apps/api/package.json traegt exakt "undici": "7.28.0"; pnpm install --frozen-lockfile --offline gruen; installierte Version 7.28.0 ✓ VERIFIED grep -n undici apps/api/package.json → "undici": "7.28.0"; node -p require(...).version → 7.28.0; pnpm install --frozen-lockfile --offline lief gruen ("Lockfile is up to date... Already up to date")
3 Spec mockt undici (Agent zeichnet options auf, fetch delegiert zur Laufzeit an globalThis.fetch); 16 bestehende Tests unveraendert gruen; 3 neue Dispatcher-Tests (dispatcher-Instanz+options, redirect manual, Singleton) ✓ VERIFIED icon-discovery.service.spec.ts Z. 12-17 (Mock-Factory exakt wie beschrieben) und Z. 225-279 (describe('IconDiscoveryService — Dispatcher (260917-jdd)') mit den drei beschriebenen Tests); vitest run src/favorites/icon-discovery.service.spec.ts → 19/19 gruen (16 bestehend + 3 neu)
4 PUT /favorites/order (@Put('order')) steht VOR @Get(':id/icon')/@Patch(':id')/@Delete(':id'); ReorderFavoritesDto mit @IsUUID()/@IsArray()@ArrayMinSize(1)@ArrayMaxSize(500)@ArrayUnique()@IsUUID('all',{each:true}); GET :id/icon sendet X-Content-Type-Options: nosniff + CSP ✓ VERIFIED Zeilennummern-Gate: Put(order)=91 < Get(:id/icon)=110 < Patch(:id)=134 < Delete(:id)=145; DTO-Datei vollstaendig gelesen — Decorators exakt wie gefordert; getIcon (Z. 129-130) setzt beide Header
5 FavoritesService.reorder() laeuft als EINE withTenantTransaction-Transaktion; findMany-Existenzabgleich; updateMany mit count===1-Pruefung; EINE BadRequestException fuer alle Abweichungsfaelle; doppelte ids scheitern VOR der Transaktion; kein forTenant() in dieser Methode ✓ VERIFIED favorites.service.ts Z. 208-243 vollstaendig gelesen — Implementierung entspricht dem Plan-Wortlaut exakt (Vorab-Duplikatpruefung, withTenantTransaction(this.prisma, tenantId, ...), findMany+existingIds-Abgleich, updateMany-Schleife mit count!==1-Wurf, Rueckgabe sortiert)
6 favorites.service.spec.ts: Fake um updateMany+withTenantTransaction erweitert; 7 neue reorder-Tests (Happy Path 0/1/2, fremde id, unbekannte id, Teilmenge, Duplikat ohne Transaktionsaufruf, fremder Mandant, Wachhund forTenant=0/withTenantTransaction=1) ✓ VERIFIED describe('reorder (260917-jdd)') Z. 536-620 gelesen — alle 7 Tests inhaltlich exakt wie im Plan beschrieben, inkl. Cross-Tenant-Test (t2 auf t1-Zeilen) und Wachhund; vitest run src/favorites → 49/49 gruen (19+30)
7 Volle API-Suite (68 Dateien/1101 Tests) und type-check gruen; rls-access-inventory.spec.ts und prisma-tenant.extension.spec.ts gruen ✓ VERIFIED pnpm --filter @tessera/api exec vitest run → "Test Files 68 passed (68), Tests 1101 passed (1101)"; pnpm --filter @tessera/api type-check → keine Ausgabe/keine Fehler; gezielt: prisma-tenant.extension.spec.ts + rls-access-inventory.spec.ts → 45/45 gruen
8 favorites-api.ts exportiert reorderFavorites(widgetId, ids): Promise<FavoriteLink[]> → PUT ${API_URL}/favorites/order, JSON-Body, credentials:'include', wirft bei !res.ok ✓ VERIFIED Datei vollstaendig gelesen Z. 78-91 — exakte Uebereinstimmung
9 Widget: FavoriteIcon mit Stufen proxy→direct→none, Buchstabe immer darunter; proxy nur bei iconUrl; onError→direct; direct nur bei getDirectFaviconSrc (http/https via new URL); onError→none; `key={iconUrl url}; kein style.display, kein dangerouslySetInnerHTML`, kein Drittanbieter-Dienst ✓ VERIFIED
10 Widget: Sortierpfeile im Bearbeitungsmodus (nur bei nicht-inline-Bearbeitung), aria-label/title aus i18n, erster/letzter deaktiviert, handleMove tauscht + setzt Position optimistisch + reorderFavorites + Fehlerpfad mit Neuladen; sichtbar in Listen- UND Kachelansicht ✓ VERIFIED handleMove Z. 137-162 exakt wie beschrieben (optimistisches Tauschen, reorderFavorites, Fehlerpfad mit fetchFavorites-Neuladen); Pfeilknoepfe Z. 556-596 in FavoriteTile, canMoveUp/canMoveDown/onMove an BEIDE sortedFavorites.map-Aufrufe (Kachel Z. 309-332, Liste Z. 338-361) durchgereicht
11 de.json/en.json: widgets.favorites.moveUpButton/moveDownButton mit echten Umlauten wo noetig ✓ VERIFIED grep -n moveUpButton|moveDownButton in beiden Dateien → Z. 307/308, Werte „Nach oben“/„Nach unten“ bzw. „Move up“/„Move down“; Umlaut-Waechter-Spec separat gruen (siehe Truth 13)
12 favorites-widget.test.tsx: Mock um reorderFavorites erweitert; 5 neue Tests (Ersatzbild bei null, Proxy→direkt→Buchstabe-Kette, kein Direktbild bei Nicht-http, Pfeilzustand+Klick, Fehlerpfad); 11 bestehende unveraendert gruen ✓ VERIFIED describe('Ersatzbild und Sortierung (quick-260917-jdd)') Z. 426-… mit exakt den 5 beschriebenen Tests (A-E); vitest run .../favorites-widget → 16/16 gruen (11+5)
13 Volle Web-Suite und type-check gruen ✓ VERIFIED pnpm --filter @tessera/web exec vitest run → "Test Files 64 passed (64), Tests 429 passed (429)"; type-check → keine Fehler; Umlaut-Guard + messages-Tests gesondert → 6/6 gruen
14 CHANGELOG (### Neu/### Behoben, Praefix „Favoriten-Widget:“); Handbuch-Tabellenzeile „Favoriten“ bleibt EINE Zeile; Zugriffsklassifikation Z. 673 Nachtrag; prisma-tenant.extension.ts Kopfkommentar-Nachtrag NUR Kommentartext ✓ VERIFIED Alle vier Diffs per git show b023d6f -- <datei> einzeln geprueft — exakte Uebereinstimmung mit Plan-Wortlaut; prisma-tenant.extension.ts-Diff zeigt AUSSCHLIESSLICH Kommentarzeilen (*-Praefix), Funktionscode unveraendert; prisma-tenant.extension.spec.ts weiterhin gruen (Teil von Truth 7)
15 Drei Commits, kein Push, kein Docker-Build, kein prisma migrate, Schema unveraendert, keine .planning/-Dateien in den Commits ✓ VERIFIED git show --stat je Commit zeigt ausschliesslich die zugesagten Dateien, keine .planning/-Pfade; git status --short zeigt .planning/-Verzeichnisse als unstaged/untracked (korrekt, nicht committet); Schema-Diff leer

Score: 15/15 automatisiert pruefbare Truths verifiziert.

  • discoverFavoriteIconUrl liefert nie null: bestaetigt am Code (favoriteUrl/fallback-Pfad in discoverFavoriteIconUrl, Z. 344-358 — jeder Fehlerpfad gibt fallback zurueck, nie null). Die Begruendung fuer die onError-Kette (nicht nur iconUrl===null) ist damit im Code nachvollziehbar, nicht nur behauptet.
  • Route-Order-Gate: rein zeilennummernbasiert bestaetigt, siehe oben — kein 404-Shadowing-Risiko (Projektgedaechtnis „NestJS Route-Order" beachtet).
  • pnpm-lock.yaml-Diff: nur der apps/api-Importer-Eintrag plus konsequente Peer-Resolution-Anpassungen an bereits vorhandenen, nicht-neuen Paketen (ews-javascript-api, http-cookie-agent — beide durch apps/api genutzt, keine neuen Downloads, keine anderen Importer-Bloecke veraendert). Deckt sich mit der Zusage „nur Importer-Eintrag von apps/api".
  • Threat-Model-Abgleich: isPublicHttpUrl, DNS-Pruefung, MAX_REDIRECTS, Timeouts, Groessendeckel — alle unveraendert im Diff sichtbar; keine Lockerung des SSRF-Schutzes gefunden.

Requirements Coverage

Requirement Beschreibung Status Evidence
QUICK-260917-JDD Symbol-Ersatzweg + Sortierung ✓ SATISFIED Alle 15 Truths oben verifiziert

Anti-Patterns Found

Keine Debt-Marker (TBD/FIXME/XXX), keine TODO/HACK/PLACEHOLDER, keine leeren Handler, kein dangerouslySetInnerHTML, kein style.display-Hack in den geaenderten Dateien gefunden. Der einzige dokumentierte Nebenbefund ist eine bereits vorbestehende Peer-Warnung (http-cookie-agent erwartet undici@^5.11.0) — keine Auswirkung auf Build/Tests, korrekt als "nicht behoben, ausserhalb des Aufgabenbereichs" im SUMMARY vermerkt.

Behavioral Spot-Checks

Behavior Command Result Status
API-Suite Favoriten (Dispatcher+Reorder) vitest run src/favorites 49/49 gruen (19 Icon-Discovery, 30 Favorites-Service) ✓ PASS
Web-Suite Favoriten-Widget vitest run src/components/.../favorites-widget 16/16 gruen ✓ PASS
RLS/Extension-Regression vitest run src/prisma/prisma-tenant.extension.spec.ts src/prisma/rls-access-inventory.spec.ts 45/45 gruen ✓ PASS
Umlaut-Waechter/messages vitest run src/messages 6/6 gruen ✓ PASS
Volle API-Suite vitest run (apps/api) 1101/1101 gruen, 68 Dateien ✓ PASS
Volle Web-Suite vitest run (apps/web) 429/429 gruen, 64 Dateien ✓ PASS
API type-check tsc --noEmit keine Fehlerausgabe ✓ PASS
Web type-check tsc --noEmit keine Fehlerausgabe ✓ PASS
Lockfile-Konsistenz pnpm install --frozen-lockfile --offline "Already up to date" ✓ PASS
Route-Order-Gate Zeilennummern-Vergleich 91 < 110 < 134 < 145 ✓ PASS
Negativ-Grep globaler fetch( grep kommentarbereinigt kein Treffer ✓ PASS
Schema unveraendert git diff --quiet 38c1400 -- schema.prisma leer ✓ PASS

Human Verification Required

Vier Punkte sind laut PLAN.md ausdruecklich als Nachweis durch den Orchestrator im Browser ausgewiesen und in dieser Umgebung (kein echter Netzzugriff/Browser) nicht pruefbar. Sie sind keine Luecken der Implementierung — die zugrundeliegende Logik (onError-Kette, Transaktionslogik) ist per Unit-Test belegt — sondern erfordern echte Netzwerk-/Browser-Bedingungen:

  1. Zertifikatsfehler-Host: Symbol erscheint ueber den Server-Proxy (icon-proxy-*), nicht ueber das Direktbild.
  2. Interner Host: Symbol erscheint ueber das Direktbild (icon-direct-*), sofern Browser-Zertifikatsvertrauen und http/https passen.
  3. Sortierung ueber Reload hinweg: neue Reihenfolge bleibt nach Neuladen der Seite erhalten.
  4. Altbestand-Normalisierung: Favoriten mit position=0 ordnen sich beim ersten Klick zu 0..n-1.

Details siehe human_verification-Block im Frontmatter.

Gaps Summary

Keine Luecken gefunden. Alle im Plan zugesagten must_haves (Wahrheiten, Artefakte, Key-Links) sind im Code nachweisbar vorhanden, korrekt verdrahtet und durch gruene automatisierte Tests belegt — einschliesslich der vollstaendigen Suiten (API 1101/1101, Web 429/429) und beider type-check-Laeufe. Die einzige offene Kategorie sind die vier Browser-Nachweise, die der Plan selbst explizit an den Orchestrator delegiert (kein Implementierungsmangel).


Verified: 2026-09-17T14:50:00Z Verifier: Claude (gsd-verifier)