Comprehensive Issue Resolution Plan
88-issue, 7-sprint resolution plan covering security, design, accessibility, bugs, and config — organized by dependency order with file-level cross-references.
Update 2026-04-25: This plan was authored before several structural moves on
apps/web(notably theproxy.ts→middleware.tsrename and Pillar A help-content redirects todocs.fragjulia.de). File paths below may not match current state — this is a spot-check, not a full audit. Treat unannotated references as "state unverified since plan was written".
88 open issues across security, design, accessibility, bugs, and config. This plan organizes them into 7 sprints with clear dependencies and priority order.
Architecture Overview
- Monorepo:
apps/web(Next.js 16 + App Router),apps/mobile(Expo),voice/(LiveKit agent),packages/shared - Stack: React 19, Supabase (auth + DB), Stripe (payments), Resend (email), Upstash Redis (rate limiting), LiveKit (voice), Tailwind v4
- Design system: V2 components in
components/v2/(Sage-Creme-Terracotta), legacy incomponents/(Pine-Gold) - Key files:
proxy.ts(middleware),lib/(rate-limit, stripe, hmac, sanitize, supabase, resend)
Update 2026-04-25:
apps/web/proxy.tsno longer exists on main; the active middleware file isapps/web/middleware.ts, which importsupdateSessionfrom@/lib/supabase/middleware. This was the launch-audit-2026-04-04 finding #1 (CRITICAL), now resolved.
Sprint 0 — Foundation & Config (Days 1–2)
Unblock everything else. Fix build, config, and env var chaos first.
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #60 | Duplicate next.config files (.ts and .mjs) | next.config.ts, next.config.mjs | S |
| #96 | next.config.ts is empty — add security headers, image domains, redirects | next.config.ts | M |
| #97 | Package name "nextjs" → "fragjulia-web" | package.json | S |
| #59 | Missing .env.example | Create .env.example | M |
| #62 | Vercel deployment returns 403 on root URL | vercel.json, next.config.ts | M |
| #112 | Multiple CSS token systems coexist | lib/fj-tokens.ts, globals.css | L |
Action plan:
- Delete
next.config.mjs(empty/stale), consolidate intonext.config.tswith security headers (X-Content-Type-Options,X-Frame-Options,Referrer-Policy), image domains, and redirects - Rename package to
fragjulia-webinpackage.json - Audit all
process.env.*usage acrosslib/andapp/api/→ create comprehensive.env.example - Fix Vercel 403 by verifying
vercel.jsonoutput directory and root route - Audit CSS token files — consolidate
fj-tokens.tsaliases, remove legacy Pine-Gold tokens, map everything to Tailwind v4 theme
Update 2026-04-25: The duplicate-config decision went the other way —
apps/web/next.config.tsno longer exists,apps/web/next.config.mjsis the active config (with security headers, image domains, redirects, and Pillar A docs.fragjulia.de redirects). Sprint 0 action plan step 1 is inverted relative to current state.
Sprint 1 — Critical Security (Days 3–5)
These are exploitable vulnerabilities. Fix before any public traffic.
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #26 | Host Header Injection in Auth Callback | app/auth/callback/route.ts | M |
| #27 | Voice Token API has no authentication | app/api/voice/token/route.ts | M |
| #28 | CRON_SECRET exposed in URL query parameter | Cron route handlers | M |
| #29 | CSP allows unsafe-inline/unsafe-eval | next.config.ts (after Sprint 0 fix) | M |
| #30 | Rate limiter silently allows all requests when Redis fails | lib/rate-limit.ts | S |
| #31 | Kliniken API string interpolation in Supabase filters | app/api/kliniken/route.ts | S |
| #54 | XSS risk in FAQ JSON-LD dangerouslySetInnerHTML | lib/json-ld.ts, FAQ page | S |
Action plan:
- #26: Replace
request.headers.get('host')with hardcodedNEXT_PUBLIC_SITE_URLorrequest.nextUrl.originfor redirect URI construction - #27: Add Supabase auth check (
createClient→getUser()) at top of voice token route; return 401 if unauthenticated - #28: Move CRON_SECRET from query param to
Authorization: Bearer <secret>header; update Vercel cron config - #29: Add strict CSP in
next.config.tsheaders — removeunsafe-inline(use nonces), removeunsafe-eval - #30: Change
lib/rate-limit.tsRedis failure mode from "allow all" to "deny" (fail-closed); add logging - #31: Use Supabase
.eq()/.ilike()parameterized methods instead of string interpolation - #54: Sanitize JSON-LD output with proper JSON escaping instead of
dangerouslySetInnerHTML
Sprint 2 — Critical UX & Broken Routes (Days 5–7)
These break core user flows. Users literally can't navigate.
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #65 | /lexikon route missing — 4+ broken links | Missing app/lexikon/ | L |
| #66 | /breastfriend/chat route missing | Missing app/breastfriend/chat/ | M |
| #67 | Chat requires login but should allow anonymous | app/chat/page.tsx, auth middleware | L |
| #68 | Header "Wissen" points to missing /lexikon | components/v2/header.tsx | S |
| #69 | 12+ pages use old AppHeader/AppFooter instead of V2 | Multiple page files | L |
| #70 | Login: duplicate "Login"/"Anmelden" buttons | app/login/page.tsx | S |
| #75 | Broken umlauts in community/live and rezepte | Multiple pages | M |
| #48 | Missing umlauts (Uberschrift, hinzufugen, Zuruck) | Multiple pages | M |
Action plan:
- #65: Create
app/lexikon/page.tsx— glossary/encyclopedia page for medical terms (placeholder with proper V2 layout, or full implementation if content data exists) - #66: Create
app/breastfriend/chat/page.tsx— chat endpoint for breastfriend matching flow - #67: Modify chat page to allow anonymous access per DesignGUIDE "anonym" promise. Move auth gate to premium features only, not base chat
- #68: Update V2 header nav to point "Wissen" to
/lexikon(or temporary redirect to/faquntil lexikon is built) - #69: Systematic migration — replace
<AppHeader/>/<AppFooter/>with<V2Header/>/<V2Footer/>in these pages:/faq,/login,/registrieren,/datenschutz,/impressum,/nutzungsbedingungen,/profil,/community,/dokumente,/kliniken,/forschung,/magazin - #70: Remove duplicate button — keep single "Anmelden" (German) CTA
- #75 + #48: Global find-and-replace for broken umlauts:
Uberschrift→Überschrift,hinzufugen→hinzufügen,Zuruck→Zurück,Loschen→Löschen,Offnen→Öffnen, etc. across all.tsxfiles
Sprint 3 — DesignGUIDE Compliance (Days 8–12)
Align all pages with the V3 DesignGUIDE spec.
3a. Color & Layout (High Priority)
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #73 | CTA buttons use sage → terracotta | components/v2/cta.tsx, button classes | M |
| #42 | Buttons should use terracotta for primary CTAs | Same as above | — |
| #74 | Chat page uses pine/gold design system | app/chat/page.tsx | L |
| #83 | Login page uses #FFFFFF — no pure white allowed | app/login/page.tsx | S |
| #43 | Max-width 1200px → 1080px | Global container class | S |
| #89 | chat_nacht/profil_nacht use hardcoded dark colors | app/chat_nacht/, app/profil_nacht/ | M |
3b. Component Specs
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #35 | Trust-Strip text deviates from spec | components/v2/trust-strip.tsx | S |
| #36 | Alltag icons 56px → 48px | components/v2/alltag.tsx | S |
| #37 | Features icons 36px → 32px | components/v2/features.tsx | S |
| #38 | FAQ max-width 800px → 760px | components/v2/faq-home.tsx | S |
| #39 | Quote attribution extra text | components/v2/quote.tsx | S |
| #40 | Header has extra "Preise" link | components/v2/header.tsx | S |
| #41 / #99 | Footer 4-column → 3-column | components/v2/footer.tsx | M |
| #76 | Evidence strip duplicate border classes | components/v2/evidence-strip.tsx | S |
| #77 | Footer text → "Daten in der EU · DSGVO-konform" | components/v2/footer.tsx | S |
| #78 | Alltag gradient values deviate | components/v2/alltag.tsx | S |
| #79 | Quote missing decorative mark + gradient | components/v2/quote.tsx | M |
| #80 | CTA missing cream bg + terracotta gradient | components/v2/cta.tsx | M |
| #95 | Hero chat preview icon (clock → message) | components/v2/hero.tsx | S |
| #98 | Feature cards hover bar terracotta CSS | components/v2/features.tsx | S |
| #100 | Hero min-h-[92vh] verification | components/v2/hero.tsx | S |
| #101 | Missing section divider lines | Homepage layout | M |
| #102 | Mobile: Hero chat preview above text | components/v2/hero.tsx | S |
| #105 | Community cards — remove icon containers | app/community/page.tsx | S |
3c. Page-Level Redesigns
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #71 | FAQ page old design system | app/faq/page.tsx | L |
| #72 | Mobile menu "Menu" → "Menü" | components/v2/header.tsx | S |
| #81 | /wer page editorial layout | app/wer/page.tsx | L |
| #82 | Registration undefined CSS var --fj-text | app/registrieren/page.tsx | S |
| #84 | Community empty state when no category | app/community/page.tsx | M |
| #86 | FAQ accordion smooth transition | components/v2/faq-home.tsx or app/faq/ | M |
| #91 | Willkommen routes both roles to same page | app/willkommen/page.tsx | M |
| #92 | Magazin old design | app/magazin/page.tsx | L |
| #93 | Rezepte emojis → SVG illustrations | app/rezepte/page.tsx | M |
| #94 | Community /neu no footer, custom header | app/community/ | S |
| #107 | Community live pages old design | app/community/ | M |
| #108 | Splash page auto-redirect | app/splash/page.tsx | S |
Action plan:
- Work through V2 components first (3b) — these cascade to all pages
- Then tackle color/layout globals (3a)
- Then page-level redesigns (3c) last, since they depend on correct components
Sprint 4 — Medium Security + Data Integrity (Days 12–14)
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #32 | Missing rate limiting on public endpoints | app/api/kliniken/, app/api/studies/, etc. | M |
| #33 | Stripe price IDs default to empty string | lib/stripe.ts | S |
| #34 | HMAC secret falls back to JWT secret | lib/hmac.ts | S |
| #52 | Login lacks client-side email validation | app/login/page.tsx | S |
| #53 | Email regex too permissive in sanitize.ts | lib/sanitize.ts | S |
| #56 | Newsletter tokens never expire | app/api/newsletter/unsubscribe/route.ts | M |
| #58 | Resend API key confusing fallback name | lib/resend.ts | S |
| #61 | Supabase admin non-null assertion | lib/supabase/admin.ts | S |
| #63 | Email send API unsafe type casting | app/api/emails/send/route.ts | M |
| #64 | Middleware error detection string matching | lib/supabase/middleware.ts or proxy.ts | M |
| #57 | Dokumente page silently catches errors | app/dokumente/page.tsx | S |
Update 2026-04-25: Issue #64's "or
proxy.ts" file alternate is stale —apps/web/proxy.tsno longer exists; the live file isapps/web/middleware.ts. The middleware logic itself still lives inlib/supabase/middleware.ts.
Action plan:
- Env var validation (#33, #34, #61): Add runtime validation at module load — throw if
STRIPE_PRICE_*,HMAC_SECRET,SUPABASE_SERVICE_ROLE_KEYare missing. Pattern:const X = process.env.X ?? (() => { throw new Error('Missing X') })() - Rate limiting (#32): Wrap public API routes (
/api/kliniken,/api/studies,/api/newsletter/*) withcheckRateLimit()fromlib/rate-limit.ts - Email validation (#52, #53): Add proper email regex to login form (client-side), tighten
lib/sanitize.tsregex to RFC 5322 subset - Newsletter tokens (#56): Add
expires_atcolumn to newsletter tokens table, validate on unsubscribe - Type safety (#63, #64): Replace
ascasts with Zod validation in email send route; replace string matching in middleware with error code checks - Resend naming (#58): Rename env var
RESEND_API_KEYconsistently, remove confusing fallback
Sprint 5 — SEO & Accessibility (Days 14–17)
5a. SEO
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #49 | 30+ pages missing metadata exports | All page.tsx files | L |
| #51 | Inconsistent datenschutz links | Multiple pages | S |
| #104 | OG image / favicon may not match spec | app/layout.tsx, public/ | M |
| #110 | Missing /kontakt and /inbox routes | Code references | S |
Action plan:
- Add
export const metadata: Metadata = { title, description, openGraph }to everypage.tsxthat lacks it - Standardize all privacy links to
/datenschutz(not/datenschutz-info) - Verify OG image and favicon against DesignGUIDE v3
5b. Accessibility (A11Y)
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #44 | Missing aria-hidden on decorative SVGs | All icon/SVG components | M |
| #45 | Form inputs missing label associations | Login, registration, chat forms | M |
| #46 / #87 | Chat missing aria-live region | app/chat/page.tsx | S |
| #47 | FAQ buttons missing aria-label | FAQ components | S |
| #85 | Missing prefers-reduced-motion | Global CSS / animation components | M |
| #103 | Breastfriend wizard progress bar ARIA | app/breastfriend/ | S |
| #109 | Keyboard focus not visible | Global CSS focus styles | M |
| #111 | Registration no password strength indicator | app/registrieren/page.tsx | M |
Action plan:
- Add
aria-hidden="true"to all decorative<svg>icons - Add
<label htmlFor>associations to all form inputs - Add
aria-live="polite"to chat message container - Add
aria-expandedandaria-labelto FAQ toggle buttons - Add
@media (prefers-reduced-motion: reduce)to disable transitions - Add visible
:focus-visibleoutline styles globally - Add password requirements display on registration form
Sprint 6 — Remaining Polish (Days 17–19)
| # | Issue | File(s) | Effort |
|---|---|---|---|
| #50 | Kliniken search lacks debounce | app/kliniken/page.tsx | S |
| #55 | Index-as-key in list renders | Multiple components | M |
| #88 | Cookie banner DSGVO compliance | components/cookie-banner.tsx | M |
| #90 | Forschung toggles non-functional | app/forschung/page.tsx | M |
| #106 | Datenschutz interactive privacy dashboard | app/datenschutz/page.tsx | L |
| #23 | P0 Resolved — verify all fixes still hold | Multiple files | M |
Action plan:
- Add 300ms debounce to kliniken search input with
useEffect+ timeout - Replace
indexkeys with stable IDs (data ID fields or generated keys) - Audit cookie banner for DSGVO: must have reject-all, granular consent, no pre-checked boxes
- Wire up forschung toggle save/revoke handlers to Supabase
- Build interactive datenschutz dashboard per DesignGUIDE
- Regression check on all P0 fixes from #23
Dependency Graph
Sprint 0 (Config/Build)
↓
Sprint 1 (Security) ←── must complete before any public deployment
↓
Sprint 2 (Broken Routes) ←── unblocks user testing
↓
Sprint 3 (DesignGUIDE) ←── depends on V2 header/footer from Sprint 2 #69
↓
Sprint 4 (Data Integrity) ←── can run parallel with Sprint 3
↓
Sprint 5 (SEO + A11Y) ←── best done after pages are stable
↓
Sprint 6 (Polish) ←── final passIssue Cross-Reference by File
Key files touched by multiple issues (merge work to avoid conflicts):
| File | Issues |
|---|---|
components/v2/header.tsx | #40, #68, #72 |
components/v2/footer.tsx | #41, #77, #99 |
components/v2/hero.tsx | #95, #100, #102 |
components/v2/alltag.tsx | #36, #78 |
components/v2/features.tsx | #37, #98 |
components/v2/quote.tsx | #39, #79 |
components/v2/cta.tsx | #42, #73, #80 |
components/v2/faq-home.tsx | #38, #86 |
next.config.ts | #29, #60, #96 |
lib/rate-limit.ts | #30, #32 |
lib/stripe.ts | #33 |
lib/hmac.ts | #34 |
lib/supabase/admin.ts | #61 |
lib/sanitize.ts | #53 |
app/login/page.tsx | #52, #70, #83 |
app/chat/page.tsx | #46, #67, #74, #87 |
app/community/ | #84, #94, #105, #107 |
app/registrieren/page.tsx | #82, #111 |
Related Issue Groups
These issues share the same root cause and should be fixed together:
- Env var validation (#33, #34, #61) — add runtime validation for all critical env vars
- Email validation (#52, #53) — client + server side
- Umlaut encoding (#48, #75) — global find/replace pass
- Footer spec (#41, #77, #99) — single footer refactor
- Chat accessibility (#46, #87) — single aria-live addition
- CTA color (#42, #73) — single terracotta migration
Estimated Effort Summary
| Sprint | Issues | Effort | Calendar |
|---|---|---|---|
| 0 — Config | 6 | ~2 days | Days 1–2 |
| 1 — Security | 7 | ~3 days | Days 3–5 |
| 2 — Broken Routes | 8 | ~2 days | Days 5–7 |
| 3 — DesignGUIDE | 35 | ~5 days | Days 8–12 |
| 4 — Data Integrity | 11 | ~2 days | Days 12–14 |
| 5 — SEO + A11Y | 12 | ~3 days | Days 14–17 |
| 6 — Polish | 6 | ~2 days | Days 17–19 |
| Total | 85 unique (3 duplicates merged) | ~19 days |
Top 5 Immediate Priorities (per user request)
- #67 — Chat login wall breaks the core "anonym" promise → Sprint 2
- #65 — /lexikon route missing (broken nav for every user) → Sprint 2
- #69 — Migrate 12+ pages to V2 header/footer → Sprint 2
- #73 — CTA buttons terracotta, not sage → Sprint 3
- #75 — Fix broken umlauts → Sprint 2
These 5 should be tackled first within their respective sprints.
Ingested from repo-root
PLAN.mdon 2026-04-25 (#647).
Voice stack architecture — single-L4 GPU layout
Three-service arrangement on one NVIDIA L4 (24 GiB), VRAM accounting under vLLM and vLLM-Omni, the runtime-download lifecycle gap for agent-side HF models, and the open architectural questions that seeded RT-1 and RT-2.
Sprint — #337 · #358 · #328 — Selfhosting · Chat · Voice
Cross-epic coordination doc for 36 open issues across Document Pipeline (#328), Chat Bedrock + Guardrails (#358), and Voice Infrastructure (#337). Identifies file-level conflicts, shared blockers, and recommended execution order.