feat/discovery
needs attentionviewing older commit2eadf2a · incrementalPR #305reviewed 2026-07-14 20:12 UTC0H · 1M · 2L · 3I- Purpose
- Land a complete Feature Discovery / Guided Tour system for the Batu platform — in-app onboarding anchors, spotlights, and tour overlays that teach users where key features live.
- Goal
- Every feature discovery anchor (data-tour attributes) is placed, registered, and covered by E2E smoke tests so onboarding works end-to-end.
- Sub-goals
- SG-discovery-domain: FCIS domain in domains/core/src/discovery/ (type, decisions, errors, queries, shells, mapper, index)
- SG-cross-domain: feature-discovery decisions, queries, shells in domains/cross-domain/
- SG-api: discovery contract + handler + mapper in apps/platform/src/api/
- SG-ui: DescubreMenu, FeatureSpotlight, GuidedTour, RpuSubTour, TourOverlay components
- SG-analytics: discovery events in packages/analytics/src/events.ts
- SG-migration: 0061_discovery_state.sql schema + preview-db seed
- SG-e2e: discovery-anchors.spec.ts smoke tests for data-tour attribute placement
- What
- Two commits tighten the E2E anchor test for export_columns: (1) b50b49c5 introduces the count-guard pattern to avoid asserting on a per-row anchor that only renders when contracts exist; (2) 2eadf2a0 refines comments to clarify the best-effort semantics and that the preview seed has no contracts.
- Why
- The export_columns anchor is on PipelineCell's per-RPU export icon — one element per data row. The preview seed creates no contracts, so in CI the table renders header-only with zero rows, making an unconditional toBeVisible() assertion flake on every run.
- Areas
- e2e/platform+56−0apps/platform/src/components/discovery+1046−0apps/platform/src/api+278−0apps/platform/src/app/[locale]/(dashboard)+342−32domains/core/src/discovery+391−0domains/cross-domain/src+993−0packages/analytics/src+92−0packages/api/src/schemas+87−0packages/database+13460−1
- Blast
- ~17 000 lines added across the discovery domain, cross-domain coordinators, API surface, 5 new UI components, analytics events, DB migration, and E2E tests. The incremental window (this review) is 1 file, ~30 lines.
Findings · 6
correctness2
Best-effort guard means CI never catches export_columns anchor regression
e2e/platform/discovery-anchors.spec.ts:41
The if (count > 0) pattern means the test only fires when export_columns elements are present. Since the preview seed has no contracts, CI always has zero rows — so this assertion is structurally disabled in the required check on every PR. The comment documents this as intentional ('catches rot on dogfooding / future seed'), but the net effect is that removing export_columns from PipelineCell would go undetected in CI until the seed gains contracts. Worth noting as a known coverage gap.
No timing gap between .count() and .first().toBeVisible()
hasTable waited 10 s for the DOM to settle. No navigation or mutation occurs between .count() and .toBeVisible(), so the count→assert pattern is timing-safe.
conventions3
Missing annotation when export_columns count is zero — inconsistent with existing pattern
e2e/platform/discovery-anchors.spec.ts:41
The hasTable=false branch pushes a skipped-assertion annotation so reviewers can see the skip in the HTML report. When the table is present but has no data rows (the CI case), the export_columns check silently no-ops with no annotation. Adding an else branch with test.info().annotations.push({ type: 'skipped-assertion', description: 'export_columns anchor skipped — no contract rows in seed' }) would make every CI run's skip visible, consistent with the existing pattern.
Structural asymmetry of inner conditional is justified
The three column-header anchors (monitoring, payment_monitoring, internal_site_ids) are on the header row and assert unconditionally inside hasTable. export_columns is on per-data-row elements — the extra inner guard is semantically correct. The updated comments explain this clearly.
Comment quality is good — distinction between header and row anchors is non-obvious
The new comments correctly distinguish between column-HEADER anchors (render when table is present, even on empty body) and per-DATA-ROW anchors (export_columns). This is non-obvious without reading PipelineCell. Comments add genuine value.
improvement1
export_columns locator constructed twice — extract to a const
e2e/platform/discovery-anchors.spec.ts:41
page.locator('[data-tour="export_columns"]') is evaluated twice (once for .count(), once for .first()). No race risk here (DOM is settled after the hasTable wait), but a one-line extraction — const exportAnchor = page.locator('[data-tour="export_columns"]') — removes duplication and matches the style used for the table locator above.
History · 36 commits
- c1fe7b7needs attentionincremental3H · 4M · 3L2026-07-27 06:02
- 414db56safeincremental0H · 0M · 2L2026-07-23 00:52
- ea68968needs attentionincremental0H · 4M · 7L2026-07-22 23:01
- 4bdb2c9needs attentionincremental1H · 3M · 5L2026-07-21 23:22
- 6e3f255safeincremental0H · 0M · 0L2026-07-20 16:46
- cc41079blockedincremental5H · 5M · 3L2026-07-20 16:25
- 2451ee0needs attentionincremental0H · 5M · 6L2026-07-20 16:09
- ab7c103needs attentionincremental0H · 1M · 6L2026-07-20 15:52
- 7eff611needs attentionincremental0H · 1M · 2L2026-07-18 00:41
- 246c67csafeincremental0H · 0M · 0L2026-07-17 23:58
- 7521b17safeincremental0H · 0M · 0L2026-07-17 23:35
- 1847ec8needs attentionincremental0H · 2M · 4L2026-07-17 23:28
- 5d3175cneeds attentionincremental3H · 6M · 4L2026-07-17 19:31
- d3f875dneeds attentionincremental1H · 2M · 4L2026-07-17 18:01
- 47d25faneeds attentionincremental1H · 1M · 2L2026-07-17 00:46
- 440d838needs attentionincremental4H · 9M · 9L2026-07-17 00:27
- 7466f97needs attentionincremental0H · 1M · 5L2026-07-16 23:20
- b686d58needs attentionincremental0H · 2M · 2L2026-07-16 14:24
- 9ea1446blockedincremental2H · 7M · 10L2026-07-16 13:44
- 6f39bb9needs attentionincremental0H · 2M · 7L2026-07-14 21:53
- 2eadf2aneeds attentionincremental0H · 1M · 2L2026-07-14 20:12current
- 97bd08fneeds attentionincremental0H · 2M · 5L2026-07-14 19:40
- a48e3ffneeds attentionincremental5H · 8M · 6L2026-07-14 18:22
- 69473b7needs attentionincremental2H · 5M · 3L2026-07-14 01:34
- 92e4235needs attentionincremental2H · 1M · 3L2026-07-14 01:04
- 6be8018safeincremental0H · 0M · 3L2026-07-14 00:15
- 090b4daneeds attentionincremental1H · 3M · 5L2026-07-13 23:50
- fa7683bneeds attentionincremental1H · 4M · 3L2026-07-13 18:55
- d64cd72needs attentionincremental2H · 3M · 2L2026-07-13 16:27
- c1f337bneeds attentionincremental3H · 7M · 14L2026-07-13 13:30
- 46e32b2safeincremental0H · 0M · 3L2026-07-11 02:54
- 3b30949needs attentionincremental0H · 2M · 3L2026-07-11 02:39
- 9f8dbdfneeds attentionincremental3H · 5M · 5L2026-07-11 02:32
- 23191eeneeds attentionincremental5H · 12M · 7L2026-07-11 02:18
- fa9b88fneeds attentionincremental0H · 1M · 2L2026-07-11 01:30
- 6a8bf42blockedfull8H · 11M · 8L2026-07-11 01:08