You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
POST /api/reminders/opened with invalid UUID reminderId
FAIL (MEDIUM) — markReminderOpened throws PostgreSQL type error; route has no try/catch; returns unhandled 500. Call is fire-and-forget from client so no UX impact, but server logs polluted.
EC10
CRON_SECRET set to empty string
PASS — empty string is falsy; returns 401
Failure Simulation
ID
Scenario
Result
FS1
DB down during trigger fan-out
PASS — Promise.allSettled isolates failures; errors counted; insertCronRun in separate try/catch
FS2
markReminderSent succeeds, insertReminder fails
PASS — accepted trade-off; documented in code comment; no duplicate notification
FS3
insertReminder succeeds, PostHog calls throw
RISK (MEDIUM) — Promise.all([trackReminderTriggered, trackReminderDelivered]) throws if PostHog is down; worker returns 500; trigger undercounts remindersSent even though DB state is correct. During demo: missing POSTHOG_KEY makes dashboard show 0 reminders sent despite all DB writes succeeding.
FS4
reorder-events DB insert fails
PASS — error caught by client; addError shown inline
FS5
DEMO_SECRET set server-side but NEXT_PUBLIC_DEMO_SECRET not in .env.local
PASS — Neon serverless uses HTTP (not persistent connections); no pool exhaustion
Performance Risks
ID
Risk
Severity
PR1
500 simultaneous fetch() calls from a single Vercel function
LOW — demo has 15 orders; real-scale behavior untested. Not a demo blocker.
PR2
insertMockOrders uses sequential await in for loop (15 iterations)
LOW — violates engineering rule #8; ~150ms slower than parallel. Not a demo blocker.
PR3
/api/seed has no auth or rate limit
LOW — ON CONFLICT DO NOTHING makes repeated calls safe; demo context only
PR4
Dashboard: 6 parallel DB queries on every render
PASS — all bounded (LIMIT 5–200); acceptable
UX Reliability
ID
Issue
Severity
UX1
Reorder page: loading / error / success states
PASS
UX2
void fetch("/api/reminders/opened") fire-and-forget in client
LOW — PostHog shows opened; dashboard stat may not increment if fetch fails. Observable discrepancy if founder cross-references PostHog and dashboard.
UX3
Funnel "X% conversion" sub-label doesn't state denominator
LOW — denominator is remindersOpened (correct), not remindersSent. Narrate during pitch: "post-open conversion rate".
UX4
Demo deep links hardcoded to orderId=2025610
LOW — requires seed to have run (it does insert 2025610). Add to demo run-book.
UX5
NEXT_PUBLIC_DEMO_SECRET visible in client JS bundle
LOW — intentional demo-scoped auth; document in README
Findings Summary
ID
Severity
Finding
Fix
QA1
Medium
/api/reminders/opened — no try/catch around markReminderOpened; invalid UUID causes unhandled 500
Wrap in try/catch; return { ok: true } even on DB error (call is fire-and-forget; analytics gap acceptable)
QA2
Medium
Worker Promise.all([trackReminderTriggered, trackReminderDelivered]) throws on PostHog failure → worker returns 500 → trigger undercounts remindersSent even though DB state is correct
Wrap PostHog calls in try/catch; log error; return 200 with { group: "test", reminderId } regardless
QA3
Low
void fetch("/api/reminders/opened") fire-and-forget — PostHog/dashboard open-rate discrepancy on failure
Acceptable for demo; document in README
QA4
Low
insertMockOrders sequential await in for loop — coding standards violation
Refactor to Promise.all(orders.map(...)) before production
QA5
Low
Funnel conversion denominator unlabeled
Add "of opens" to sub-label, or narrate during pitch
Final QA Verdict
PASS
No high-risk blockers. Core end-to-end demo flow verified:
POST /api/seed
→ POST /api/reorder-trigger (CRON_SECRET)
→ workers assign cohorts, insert reminders, fire PostHog
→ dashboard shows funnel stats (test + control split)
→ /reorder/[orderId] renders correct product
→ Add to Cart → order_placed → North Star updates
→ ControlGroupSimulator fires control_order_placed (idempotent)
2 medium findings (QA1, QA2) should be fixed before demo run. 3 low findings are informational.