Files
kochwas/docs/superpowers/plans/2026-04-19-post-review-roadmap.md
hsiegeln 2c1fd29003
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 30s
docs(plan): Post-Review-Roadmap fuer Items A-I
Sequenziert die nach review-fixes-2026-04-18 offenen Punkte aus
OPEN-ISSUES-NEXT.md in 5 Tiers: Cleanup-Batch (I+H+F+G) direkt
nach Merge, Search-State-Store als eigene Phase, SearXNG-Recovery
reaktiv, Rest trigger-basiert.

Jedes Item hat Scope, Files, Gate und Aufwand — tief genug fuer
/gsd-plan-phase als naechsten Schritt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:34:19 +02:00

10 KiB
Raw Blame History

Post-Review Roadmap 2026-04-19

Quelle: docs/superpowers/review/OPEN-ISSUES-NEXT.md (Items AI) + UAT kochwas-dev.siegeln.net (Branch review-fixes-2026-04-18, 2026-04-19). Branch-Status: Merge-ready — 8 atomare Commits, 184/184 Tests grün, svelte-check 0 Errors, UAT durchgeklickt (Profil, Suche, Rezept-Actions, Wunschliste, Preview, Admin, API-Shapes). Goal: Die nach dem Review-Branch offenen 9 Items in priorisierte Phasen übersetzen, damit jede einzeln via /gsd-plan-phase/gsd-execute-phase abgearbeitet werden kann. Architecture: Keine Groß-Refactor-Phase, sondern getaktete Einzel-Phasen mit klarem Gate. Reihenfolge folgt Risiko × Wert: erst kleine Wins, dann eine strukturelle Phase (A), dann opportunistische. Tech-Stack: SvelteKit, TypeScript-strict, Zod, Vitest, Playwright-UAT, better-sqlite3, Service-Worker.


Merge-Entscheidung

Jetzt mergen. Der Branch-UAT auf kochwas-dev war clean (siehe Session-Log 2026-04-19). Findings aus dem UAT:

  • Kommentar-Delete hat keinen UI-Button (MINOR, kein Branch-Regress — Zustand schon vor Refactor so).
  • /preview ohne ?url= bleibt im Dauer-Lader (MINOR, harmlos — niemand ruft die Route blank auf).

Beide werden als LOW-Items unten aufgenommen, sind aber kein Merge-Blocker.


Tier-Zuordnung

Tier Items Wann Aufwand total
1 — Schneller Cleanup-Batch F, G, H, I Direkt nach Merge ~2 h
2 — Phase Search-State-Store A Nächster größerer Slot halber Tag
3 — Phase SearXNG-Recovery C Wenn Rate-Limit-Schmerz konkret auftaucht 12 h
4 — Opportunistisch B, D, E, + Kommentar-Delete, Preview-Guard Trigger-basiert reaktiv
5 — Geparkt yauzl / Phase 5b Nur bei explizitem Bedarf nicht geplant

Tier 1 — Cleanup-Batch (1 Phase, 4 Items)

Phasenname-Vorschlag: Phase Cleanup-Batch nach Review-Fixes (via /gsd-new-phase oder /gsd-add-phase).

Alle vier Items touchen wenige Zeilen, sind LOW/MEDIUM, und lassen sich in 12 Commits pro Item sauber atomar committen. Gebündelt statt einzeln, weil Kontext-Overhead pro Einzelphase größer wäre als der Fix.

Item I — RecipeEditor auf $derived umstellen

Files: src/lib/components/RecipeEditor.svelte:28,97102,113,121, src/routes/recipes/[id]/+page.svelte:43

Pattern aktuell: let foo = recipe.bar → Svelte-5-Warning, Snapshot-only, bricht bei In-Place-Mutation des Rezepts.

Plan pro Warnung:

  • Warning-Site auslesen, beurteilen: soll foo Mutations am recipe tracken oder bewusst ein Snapshot bleiben?
  • Track-Fall: let foo = $derived(recipe.bar).
  • Snapshot-Fall: Variable umbenennen (z. B. initialFoo) und als $state deklarieren mit Kommentar // intentional snapshot.
  • npm run check — 0 Warnings erwartet.
  • npm test — grün.
  • Commit: refactor(editor): RecipeEditor auf $derived umstellen.

Gate: svelte-check 0 Warnings, alle Editor-Flows (Titel, Zutaten, Schritte) per Hand getestet — In-Place-PATCH zeigt aktualisierten Wert.

Item H — RecipeEditor Bild-Upload/Delete auf asyncFetch

Files: src/lib/components/RecipeEditor.svelte:54,83

Warum zusammen mit I: Gleiche Datei, gleicher Touch.

  • Zeile 54 (Upload): const res = await fetch(...); if (!res.ok) alertAction(...)await asyncFetch(...).
  • Zeile 83 (Delete): dito.
  • Error-Messages beibehalten.
  • Test manuell: Bild hochladen + löschen in einem Test-Rezept.
  • Commit: refactor(editor): Bild-Upload/Delete auf asyncFetch.

Gate: Bild-Upload + Delete-Flow grün in manuellem Smoke; npm run check clean.

Item F — Inline UI-Constants in src/lib/theme.ts

Files: Neu src/lib/theme.ts, Modify ConfirmDialog.svelte, ProfileSwitcher.svelte, weitere Call-Sites via grep.

  • grep -rn "z-index:\|border-radius: 999\|setTimeout.*[0-9]{3,4}" src/lib/components src/routes — Call-Sites auflisten.
  • src/lib/theme.ts anlegen mit: MODAL_Z_INDEX = 1000, POPOVER_Z_INDEX = 900, PILL_RADIUS = '999px' (nur Werte, die wirklich mehrfach vorkommen — YAGNI).
  • Call-Sites durchgehen, Inline-Werte durch Import ersetzen.
  • npm run check + npm test.
  • Commit: refactor(ui): shared theme constants fuer z-index/radius.

Gate: Keine visuellen Änderungen beim Durchklicken (Confirm-Dialog, Profile-Switcher, Toast, Menü).

Item G — requireProfile() mit optionaler Message

Files: src/lib/client/confirm.svelte.ts (oder wo requireProfile liegt), src/routes/wishlist/+page.svelte:38

Option A — minimal invasiv: wishlist/+page.svelte belassen, Custom-Message-Konstante in der Datei. Dann nur dokumentieren im Kommentar der requireProfile-Funktion, dass die Wunschliste bewusst eigenständig ist.

Option B — DRY: requireProfile(message?: string): Profile | null mit Fallback auf Default.

  • Entscheidung zuerst — Option A sparsamer, Option B konsistent. Ich empfehle A, weil die Custom-Message in der Wunschliste wirklich Kontext ist („um mitzuwünschen"), nicht nur Deko. Aber: wenn B, dann sauber mit Unit-Test.
  • Commit: refactor(client): requireProfile Custom-Message entscheiden (je nach Entscheidung).

Gate: Wunschliste zeigt beim Klick ohne Profil die korrekte Message; keine anderen Sites verhalten sich anders.


Tier 2 — Phase Search-State-Store (Item A)

Empfohlener Einstieg: /gsd-discuss-phase Search-State-Store (per OPEN-ISSUES Empfehlung), nicht direkt /gsd-plan-phase.

Warum eigene Phase: Touch +page.svelte (808 L) + +layout.svelte (678 L), Reactive-Glue zwischen Header-Search-Dropdown und Home-Search muss 1:1 übernommen werden. UAT-pflichtig, weil es keine UI-Tests gibt.

Scope-Sketch (für die Discuss-Phase):

  • Neu: src/lib/client/search.svelte.ts — reaktiver Store mit query, hits, loading, error, hasMore, search(q), loadMore(), clear().
  • Debounce (aktuell in +page.svelte) in den Store migrieren.
  • Web-Fallback-Logik (lokal leer → Web-Suche) beibehalten — Store muss beide Modi kennen (mode: 'local' | 'web').
  • +layout.svelte Header-Dropdown zuerst migrieren (kleineres Surface), dann +page.svelte.
  • Duplizierten $state-Block entfernen.

Verifikation pro Wave:

  1. Nach Store-Anlegen: Vitest-Unit-Tests für Store (mocked fetch).
  2. Nach Layout-Migration: Browser-UAT Header-Dropdown auf Rezept-Seite + Startseite.
  3. Nach Page-Migration: Browser-UAT Live-Suche (lokaler Treffer, Web-Fallback, Empty-State), inkl. Deep-Link ?q=xyz.
  4. Playwright-Script wiederholen (existiert aus 2026-04-19 UAT).

Gate: Alle 3 UAT-Pfade clean; +page.svelte unter 700 L; +layout.svelte unter 600 L; npm test + npm run check grün.

Aufwand: halber Tag (46 h).


Tier 3 — Phase SearXNG-Rate-Limit-Recovery (Item C)

Trigger: Wenn konkreter Schmerz (User merkt „Suche liefert komische alte Sachen" oder SearXNG logt 429/403 gehäuft).

Scope:

  • src/lib/server/search/searxng.ts: lastFailureAt: Map<string, number> pro Domain.
  • Exponentieller Backoff: bei wiederholtem 429/403 → 1 min → 5 min → 30 min (Cap).
  • Response-Shape erweitern: isStale?: boolean wenn aus Cache nach Fail.
  • UI: src/routes/+page.svelte Such-Ergebnisheader zeigt „Ergebnisse evtl. veraltet" wenn isStale.

Tests (TDD, Vitest):

  • Simulierter 429 → nächster Call innerhalb 1 min geht nicht raus, Response aus Cache mit isStale: true.
  • Nach 1 min Wartezeit → Call geht wieder raus.
  • Nach erfolgreichem Call → Backoff-Zähler resettet.

Gate: Tests grün; manuell: Fake-429 injizieren (z. B. über ENV-Toggle im Dev), UI zeigt Hinweis.

Aufwand: 12 h.


Tier 4 — Opportunistisch (Trigger-gesteuert)

Alle Items hier werden nicht proaktiv geplant. Sie warten auf ihren Trigger.

Item B — RecipeEditor/RecipeView in Sub-Components

Trigger: Zweite Person arbeitet am Projekt mit, ODER Editor-Bug-Hunt wird unübersichtlich.

Scope-Sketch: IngredientRow.svelte, StepList.svelte, TimeDisplay.svelte, ImageUploadBox.svelte.

Vorbedingung: Item I muss zuerst durch sein (die pre-existing Warnings würden sonst in die Sub-Components wandern).

Item D — SW Zombie-Cleanup unter Drosselung

Trigger: Nächster Service-Worker-Touch (z. B. neue Cache-Strategy oder Chunks-Manifest-Änderung).

Scope: Mit DevTools-Throttling-Profil „Slow 3G" durchgehen, prüfen ob der 1500ms-Timeout in pwa.svelte.ts False-Positives triggert. Falls ja: Timeout konfigurierbar oder Heuristik verfeinern.

Item E — JSON-LD Parser Locale-Edge-Cases

Trigger: Echter Import-Bug aus dem Alltag.

Scope: Gezielter Test für die Fail-URL + Fix. Kein Vorab-Sprint.

Kommentar-Delete-UI (UAT 2026-04-19)

Status: Kommentar-DELETE-Endpoint existiert, aber keine UI-Exposition.

Vorschlag: In src/routes/recipes/[id]/+page.svelte Kommentar-Liste pro Eintrag ein 🗑-Button für den Autor (comment.profile_id === profileStore.active?.id). Mit confirmAction-Dialog.

Trigger: Erster Wunsch, einen Kommentar loszuwerden.

Aufwand: ~30 min.

Preview-ohne-URL-Guard (UAT 2026-04-19)

Status: /preview ohne ?url= bleibt im Dauer-Lader.

Vorschlag: src/routes/preview/+page.svelte Zeile 33ff.: wenn u leer, errored = 'Kein URL-Parameter gesetzt' oder Redirect auf /. 2-Zeilen-Fix.

Trigger: Bevor jemand die Route bookmarked.

Aufwand: 5 min — könnte man auch sofort in Tier 1 reinnehmen, ist aber so trivial, dass es ohne Phase geht.


Tier 5 — Geparkt

Phase 5b — ZIP-Backup-Restore via yauzl

Status: Dokumentiert in ARCHITECTURE.md:33 und session-handoff-2026-04-17.md. Dependency bleibt installiert.

Kein Plan. Wird erst aktiviert, wenn jemand wirklich ein Backup-ZIP zurückspielen will. Dann: /gsd-plan-phase Phase-5b-ZIP-Restore.


Empfohlene Ausführungs-Reihenfolge

  1. Merge review-fixes-2026-04-18main.
  2. Neuen Branch cleanup-batch-post-review → Tier 1 (Items I + H zusammen in einem Wave, dann F, dann G).
  3. Merge → Tier 2 Discuss: /gsd-discuss-phase Search-State-Store.
  4. Tier 2 execution.
  5. Tier 3 erst wenn der Trigger da ist, sonst Tier 4 abwarten.

Commit-Stil für alle Phasen

  • Deutsch, kleinteilig, eine Idee pro Commit.
  • Body erklärt das Warum (Reference auf Item-Nummer aus diesem Doc).
  • Nach jedem Commit npm test + npm run check grün.
  • Push direkt nach Commit (CI baut Branch-Tag, siehe docker.yml).