Skip to content

refactor(web): dissolve domains/nudges/ into shared top-level directories (LUM-1919)#32204

Merged
ashleeradka merged 1 commit into
mainfrom
devin/1779840432-dissolve-nudges-domain
May 27, 2026
Merged

refactor(web): dissolve domains/nudges/ into shared top-level directories (LUM-1919)#32204
ashleeradka merged 1 commit into
mainfrom
devin/1779840432-dissolve-nudges-domain

Conversation

@ashleeradka
Copy link
Copy Markdown
Contributor

Prompt / plan

Why needed: The domains/nudges/ directory was consumed by 4 other domains (onboarding, settings, chat, and internally by nudges itself). Per CONVENTIONS.md — "code used by 2+ domains moves to top-level shared directories." Rather than surgically extracting individual exports, the entire domain is dissolved because virtually every file had cross-domain consumers.

What changed:

Source Destination Rationale
ios-app-platform.ts + mac-app-platform.ts utils/platform-detection.ts Pure browser/OS detection — not nudge-specific. Used by onboarding, settings, chat. Merged into one file since macOS detection depends on iOS detection.
nudge-store.ts stores/nudge-store.ts Cross-domain Zustand store consumed by GitHub/Discord hooks and settings/community-page. Legacy-cleanup key strings inlined to avoid circular dependency with hook files.
nudge-prefs.ts utils/nudge-prefs.ts Typed localStorage helpers used by iOS + macOS nudge modules.
github-constants.ts + github-prefs.ts hooks/use-github-nudge.ts Constants merged into hook file — reduces file count, constants are tightly coupled to the hook logic.
discord-constants.ts + discord-prefs.ts hooks/use-discord-nudge.ts Same pattern as GitHub.
ios-app-constants.ts + ios-app-prefs.ts hooks/use-ios-app-nudge.ts Same pattern.
mac-app-constants.ts + mac-app-prefs.ts hooks/use-macos-app-nudge.ts Same pattern.
components/*.tsx (5 files) components/nudges/*.tsx Banner UI components — moved as-is with updated internal imports.

What stayed the same: All behavior is preserved — no logic changes, just file moves and import path updates.

Alternatives not taken:

  • Conservative lift (platform detection only): Would have fixed only 4 of 7 violations and left a hollow nudges domain with most of its exports consumed externally. Dissolving is cleaner.
  • Separate constant files in utils/: Considered keeping github-nudge-constants.ts, discord-nudge-constants.ts, etc. as separate files. Rejected because constants are small (2–6 per module), tightly coupled to their hook, and merging reduces file count from 17 → 12. The store's dependency on constants was broken by inlining the literal localStorage key strings in the legacy-cleanup section.
  • Keeping nudges as a domain with lifted shared exports: Would require splitting each prefs file (shared functions vs nudge-specific hooks), creating more fragmentation without clear benefit.

Cross-domain violation accounting: 7 "nudges" entries removed from allowlist (95 → 88 violations remaining). 25 files changed, net -92 lines.

References:

Test plan

  • bunx tsc --noEmit — passes clean
  • bun run lint — passes clean
  • bun run audit:cross-domain — regenerated allowlist confirms 88 imports (down from 95)
  • Pre-commit hooks (lint + typecheck) pass on commit

Closes LUM-1919

Link to Devin session: https://app.devin.ai/sessions/4b3b130bbf274e5487da3b4992883093
Requested by: @ashleeradka

…ries (LUM-1919)

Dissolves the nudges domain entirely — every file consumed by 2+ domains
now lives in a top-level shared directory:

- Platform detection (isIOSBrowser, isMacOSBrowser, useIsIOSWeb,
  useIsMacOSWeb) → utils/platform-detection.ts
- Nudge Zustand store → stores/nudge-store.ts
- GitHub/Discord/iOS/macOS nudge hooks + prefs → hooks/use-*-nudge.ts
- localStorage helpers → utils/nudge-prefs.ts
- Banner UI components → components/nudges/

Constants merged into their respective hook files to reduce file count;
legacy-cleanup key strings inlined in the store to avoid circular deps.

7 cross-domain violations eliminated (95 → 88).

Closes LUM-1919

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@linear
Copy link
Copy Markdown

linear Bot commented May 27, 2026

LUM-1919

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Test Results — LUM-1919 Nudge Domain Dissolution

Method: Vite dev server (port 3001) + Playwright CDP route interception
CI: All green (7/7)

Results: All 3 tests passed

  • Community page (/assistant/settings/community) — PASSED
    • "Star on GitHub" button, "Join Discord" button, hero banner all rendered
    • Proves use-github-nudge.js and use-discord-nudge.js imports resolve correctly
  • Chat page (/assistant/) — PASSED
    • Page renders with composer and greeting, no error boundary
    • Proves all 4 banner component imports + use-app-nudges.ts full import chain works
  • Settings page (/assistant/settings) — PASSED
    • Page renders without crash, iOS card correctly hidden on desktop
    • Proves platform-detection.ts import resolves and useIsIOSWeb() returns false
  • Console — ZERO nudge/import errors (only expected 404s from unmocked API routes)
Community page screenshot

Community page

Chat page screenshot

Chat page

Console verification (chat page)

Console

All errors are expected 404s from unmocked /v1/ API routes. Zero errors referencing module resolution, nudge files, platform-detection, or nudge-store.

Devin session

Copy link
Copy Markdown
Contributor

@vex-assistant-bot vex-assistant-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE

Value: Dissolves a domain that was breaking its own convention — domains/nudges/ was consumed by 4 other domains, a violation of the "2+ domains → shared" rule. After this PR: platform detection lives in utils/, the Zustand store in stores/, the nudge hooks are self-contained, and every one of the 19 cross-domain allowlist entries for nudges is gone.

This is a pure refactor — Devin confirms no issues, no bot inline comments, and my read of the files agrees. Spot-checks:

platform-detection.ts

  • isMacOSBrowser() calls isIOSBrowser() at the top of the function — iPadOS 13+ (which sends a macOS UA but has multitouch) is correctly excluded from the macOS nudge. The dependency is within the same file now, no import dance needed.
  • useSyncExternalStore(noop, ...) is the right pattern for values that don't change after hydration — subscribe is a no-op because platform detection is stable for the session. Server snapshot returns false (SSR safe).

nudge-store.ts

  • createSelectors(useNudgeStoreBase) — canonical Zustand v5 selector pattern ✅
  • vellum:nudge-prefs starts with vellum → cleared on logout by session-cleanup.ts. Nudge state is correctly user-scoped.
  • app.nudgeLegacy.cleaned starts with app. → survives logout. Correct: if we already cleaned up stale keys, there's no reason to repeat it per-user.
  • Legacy key strings inlined directly — avoids circular dependency with the hook files, documented in a comment.

use-app-nudges.ts

  • Import paths point to @/utils/platform-detection.js, @/hooks/use-ios-app-nudge.js, etc. — all match the new file locations. No stale domains/nudges/ references.
  • Cascading visibility logic (platform → GitHub → Discord) is unchanged.

cross-domain-allowlist.json

  • All 19 nudges entries removed. No other domain now has a cross-domain dependency on nudges.

utils/nudge-prefs.ts — one note (non-blocking): readBooleanPref/writeBooleanPref duplicate the logic just added by PR #32199 (getLocalBool/setLocalBool in local-settings.ts). These could be collapsed in a follow-up. The outer try/catch wrappers are also redundant since getLocalSetting/setLocalSetting already swallow exceptions internally — harmless but worth a cleanup pass once #32199 lands.

Vellum Constitution — Legibility: the codebase now says what it means. Code shared by 4 domains lives in shared directories, not inside one of them.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

Re: nudge-prefs.ts dedup note — good catch. PR #32199 hasn't landed yet (getLocalBool/setLocalBool don't exist in the codebase), so the dedup is a follow-up for after that PR merges. The redundant try/catch wrappers are harmless in the meantime.

@ashleeradka ashleeradka merged commit a427873 into main May 27, 2026
7 checks passed
@ashleeradka ashleeradka deleted the devin/1779840432-dissolve-nudges-domain branch May 27, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant