← all branches

feat/discovery

needs attentionviewing older commit
fa9b88f · incrementalpre-PRreviewed 2026-07-11 01:30 UTC0H · 1M · 2L
The branch
Purpose
Guide users to features they haven't discovered — contextual spotlight popups that surface at the right moment on the right page, server-side paced to avoid spam.
Goal
Add Feature Discovery v3 to the platform: DB-backed discovery state, FCIS domain, API contract, and a FeatureSpotlight coachmark component gated by a PostHog flag.
Sub-goals
  • SG-1: DB schema + FCIS domain (discovery_state table, decisions/queries/shells/mapper/type-check, tests)
  • SG-2: API contract + handler + mapper, FeatureSpotlight UI coachmark, analytics events, e2e anchor tests
  • SG-fix: PostHog bypass on Vercel previews — flag never evaluates there (no NEXT_PUBLIC_POSTHOG_KEY), so previews get the surface unconditionally for dogfooding
The changes (whole branch)
What
Adds isPreviewHost() to spotlight-catalog.ts and wires it into the enabled guard in FeatureSpotlight.tsx so Vercel preview deployments show spotlights without needing the PostHog flag active.
Why
PostHog is deliberately not initialized on Vercel preview environments to keep preview traffic out of prod analytics. Without this fix the entire discovery surface was invisible on every preview deploy, blocking dogfooding and QA.
Areas
apps/platform+5612domains/core/src/discovery+5990domains/cross-domain/src+9140e2e/platform+460packages/analytics+420packages/api+630packages/database+1340packages/shared-kernel+10scripts+300
Blast
~2390 meaningful lines of new code across 9 subsystems (excl. 13k-line drizzle meta snapshot); 1 new DB table (discovery_state, migration 0058); PostHog flag gates prod rollout.
new-db-migration posthog-flag-gated preview-bypass
ci· pre-PR branch — CI checks not availablecoderabbit· no .coderabbit.yaml in repo

Findings · 3

tests1

medium

isPreviewHost() has no unit test

apps/platform/src/components/discovery/spotlight-catalog.ts

isPreviewHost() is a pure, exported function with two distinct behaviors (window undefined → false; hostname suffix match → boolean) and a non-obvious implementation choice: hostname sniffing rather than NEXT_PUBLIC_VERCEL_ENV because the env var's availability depends on a Vercel project setting. A unit test with jsdom's window.location mock would document the invariants — 'app.batuenergy.com → false', '*.vercel.app → true', 'SSR → false' — and catch regressions. The existing test suite already covers resolveSpotlightPage (same file, same pattern); isPreviewHost deserves the same treatment. A silent regression (always returning false) would make QA on preview branches impossible without any CI signal.

improvement2

low

isPreviewHost() called on every render without memoization

apps/platform/src/components/discovery/FeatureSpotlight.tsx:73

isPreviewHost() reads window.location.hostname on every render. The hostname is invariant for the page lifetime, so a module-level constant or useMemo(()=>isPreviewHost(),[]) would stabilize it. All other derived booleans in FeatureSpotlight (flagEnabled, isDesktop) are already stabilized via useSyncExternalStore/useCallback; isPreviewHost is the odd one out. Low priority given the trivial cost, but worth keeping consistent.

low

Inline comment redundant with isPreviewHost JSDoc

apps/platform/src/components/discovery/FeatureSpotlight.tsx:71

The two-line comment above the enabled assignment duplicates the JSDoc on isPreviewHost() in spotlight-catalog.ts. A reader following the import already gets the full explanation. Reducing to '// preview bypass — see isPreviewHost()' or removing the comment entirely would keep the call site clean.

History · 36 commits

  1. c1fe7b7needs attentionincremental3H · 4M · 3L2026-07-27 06:02
  2. 414db56safeincremental0H · 0M · 2L2026-07-23 00:52
  3. ea68968needs attentionincremental0H · 4M · 7L2026-07-22 23:01
  4. 4bdb2c9needs attentionincremental1H · 3M · 5L2026-07-21 23:22
  5. 6e3f255safeincremental0H · 0M · 0L2026-07-20 16:46
  6. cc41079blockedincremental5H · 5M · 3L2026-07-20 16:25
  7. 2451ee0needs attentionincremental0H · 5M · 6L2026-07-20 16:09
  8. ab7c103needs attentionincremental0H · 1M · 6L2026-07-20 15:52
  9. 7eff611needs attentionincremental0H · 1M · 2L2026-07-18 00:41
  10. 246c67csafeincremental0H · 0M · 0L2026-07-17 23:58
  11. 7521b17safeincremental0H · 0M · 0L2026-07-17 23:35
  12. 1847ec8needs attentionincremental0H · 2M · 4L2026-07-17 23:28
  13. 5d3175cneeds attentionincremental3H · 6M · 4L2026-07-17 19:31
  14. d3f875dneeds attentionincremental1H · 2M · 4L2026-07-17 18:01
  15. 47d25faneeds attentionincremental1H · 1M · 2L2026-07-17 00:46
  16. 440d838needs attentionincremental4H · 9M · 9L2026-07-17 00:27
  17. 7466f97needs attentionincremental0H · 1M · 5L2026-07-16 23:20
  18. b686d58needs attentionincremental0H · 2M · 2L2026-07-16 14:24
  19. 9ea1446blockedincremental2H · 7M · 10L2026-07-16 13:44
  20. 6f39bb9needs attentionincremental0H · 2M · 7L2026-07-14 21:53
  21. 2eadf2aneeds attentionincremental0H · 1M · 2L2026-07-14 20:12
  22. 97bd08fneeds attentionincremental0H · 2M · 5L2026-07-14 19:40
  23. a48e3ffneeds attentionincremental5H · 8M · 6L2026-07-14 18:22
  24. 69473b7needs attentionincremental2H · 5M · 3L2026-07-14 01:34
  25. 92e4235needs attentionincremental2H · 1M · 3L2026-07-14 01:04
  26. 6be8018safeincremental0H · 0M · 3L2026-07-14 00:15
  27. 090b4daneeds attentionincremental1H · 3M · 5L2026-07-13 23:50
  28. fa7683bneeds attentionincremental1H · 4M · 3L2026-07-13 18:55
  29. d64cd72needs attentionincremental2H · 3M · 2L2026-07-13 16:27
  30. c1f337bneeds attentionincremental3H · 7M · 14L2026-07-13 13:30
  31. 46e32b2safeincremental0H · 0M · 3L2026-07-11 02:54
  32. 3b30949needs attentionincremental0H · 2M · 3L2026-07-11 02:39
  33. 9f8dbdfneeds attentionincremental3H · 5M · 5L2026-07-11 02:32
  34. 23191eeneeds attentionincremental5H · 12M · 7L2026-07-11 02:18
  35. fa9b88fneeds attentionincremental0H · 1M · 2L2026-07-11 01:30current
  36. 6a8bf42blockedfull8H · 11M · 8L2026-07-11 01:08