← all branches

feat/discovery

needs attentionviewing older commit
92e4235 · incrementalPR #305reviewed 2026-07-14 01:04 UTC2H · 1M · 3L · 2I
The branch
Purpose
Contextual feature discovery for existing users — ambient spotlights, Descubre hub, and first-run guided product tour. Entirely flag-gated (feature-discovery-spotlights, OFF by default); zero user impact until flag is flipped.
Goal
Feature Discovery v1: surface unused capabilities to existing users via coachmarks, a gap-aware hub, and a 5-step product tour — FCIS domain layer + PostHog flag gating throughout.
Sub-goals
  • SG-1: Ambient spotlights — contextual coachmarks for unused capabilities, paced ≤1/rolling-day, retire-on-dismiss
  • SG-2: Descubre hub — header badge + gap listing + Volver a tomar el tour re-entry
  • SG-3: First-run linear product tour — fixed 5 steps, scrim + spotlight cutout, Hazlo ahora shortcuts
  • SG-4: FCIS domain layer — discovery_state table (migration 0058), pure decisions, RLS, JSend API
  • SG-5: Adversarial review fixes — VERCEL_ENV gating, spotlight self-destruct latch, atomic upsert, gate-key exemption
  • SG-6 (this commit): Bug fix — last tour step auto-closed on missing anchor; degrade to centered modal instead
The changes (whole branch)
What
Single commit: anchor-miss behavior in TourOverlay changed from auto-advance/complete to graceful centered-modal degradation. Dep array tightened from [step.anchor, step.id, stepIdx, isLast, onStep, onComplete] to [step.anchor, step.id].
Why
The last tour step pointed at the Descubre header badge, which disappears once the gap is empty. The old auto-complete path closed the tour before the user finished reading the final step — unread content silently skipped.
Areas
apps/platform+150220domains/cross-domain+13120domains/core+4980packages/secrets+15212packages/database+1310packages/api+890packages/analytics+720e2e/platform+480
Blast
51 files, +3835/-32 across 8 areas. Feature is flag-gated (fails closed if flag absent) — zero user impact until flag is explicitly flipped.
flag-gated: feature-discovery-spotlights (OFF by default, fails closed) deploy preconditions before flag flip: create PostHog flag + run 2-row org reclassify SQL
CI· no CI data available from runnerCodeRabbit· no .coderabbit.yaml in repo

Findings · 8

correctness2

low

Dep-array narrowing assumes unique step IDs — fragile for future steps

apps/platform/src/components/discovery/GuidedTour.tsx:193

Removing stepIdx from deps is safe because step.id uniquely tracks stepIdx changes. If a future step were added with a duplicate id, the effect would silently fail to re-run on that transition, leaving rect stale. Adding stepIdx back would cost nothing and remove the implicit assumption.

info

Centered fallback has no scroll path if the card's nav buttons are off-viewport

apps/platform/src/components/discovery/GuidedTour.tsx

Not a regression — card UI is unchanged. If a future step body is very long and pushes Siguiente off-screen while the card is centered (no scroll), a user could appear stuck. Worth checking card max-height for steps that may lack anchors.

conventions3

low

Comment block uses all-caps (NEVER, CENTERED) inconsistent with surrounding style

apps/platform/src/components/discovery/GuidedTour.tsx:183

The 5-line comment explains a genuinely non-obvious invariant (appropriate per codebase rule), but NEVER and CENTERED in all-caps are inconsistent with the surrounding code style. Downcasing to prose would match conventions while preserving the WHY explanation.

low

Confirm exhaustive-deps lint is not suppressed for the narrowed dep array

apps/platform/src/components/discovery/GuidedTour.tsx:193

The dep array intentionally omits stepIdx, isLast, onStep, onComplete. Verify eslint-plugin-react-hooks exhaustive-deps is active for this file — future edits that reintroduce references to those identifiers should get a lint error, not a silent stale-closure.

info

analytics: discovery.spotlight_anchor_missing no longer implies auto-completion

apps/platform/src/components/discovery/GuidedTour.tsx

The existing event still fires on miss — observability is intact. But if any PostHog query used this event as a tour-completion proxy, that interpretation is now incorrect. Worth a one-time audit of any dashboards querying this event.

tests3

high

No unit test: onComplete must NOT be called on anchor miss

apps/platform/src/components/discovery/GuidedTour.tsx:180

The fix's core invariant — that a missing anchor leaves the tour open rather than auto-completing it — has no automated verification. Render TourOverlay with a step whose anchor matches nothing, advance fake timers past the 8x250ms retry window, and assert both onComplete and onStep are never called.

high

No unit test: centered-modal fallback when anchor is absent

apps/platform/src/components/discovery/GuidedTour.tsx:180

The positive assertion — that rect stays null and the card renders in centered layout (no spotlight positioning) — is also untested. A component test should assert the fallback card is visible after the retry timeout, confirming the UX degradation is correct.

medium

Playwright anchor smoke doesn't cover the missing-anchor degradation path

e2e/platform/discovery-anchors.spec.ts

The spec verifies anchors ARE present (a CI tripwire), but doesn't exercise what happens when one is absent — the exact scenario this fix addresses. A dedicated component test or E2E test covering the degradation path would lock in the fix.

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:04current
  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:30
  36. 6a8bf42blockedfull8H · 11M · 8L2026-07-11 01:08