Conversation
Move and update docs
…ing-ref-factories sdk-core string ref replacements
…al-convex-wrapper-hooks introduce-mobile-local-convex-wrapper-hooks
* Manual edits to email capture widget (+ CI summary labels text) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Djanogly <45178753+djanogly@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…TBC) (#24) * Add file uploads - UI needs work * improve styling, remove Snippets button * Update unit tests * skip dismiss email collection test * fallback and tweaks * Limit filetypes Partially completed - the upload spoofing hardening is improved, but not all the way to magic-byte sniffing. We now require an allowlisted extension from the normalized filename on both the client and backend in supportAttachments.ts (line 70) and supportAttachments.ts (line 57), and added regression coverage in supportAttachments.test.ts (line 151) and supportAttachments.test.ts (line 8). I did not add signature checks, because the current finalizeUpload boundary is a Convex mutation and ctx.storage.get() is only available in actions. Doing true magic-byte validation would need a larger refactor of that finalize flow.
* Proper AI knowledge handling with Mastra * feat: replace legacy AI agent knowledge retrieval with Convex Vector Search * docs: update spec to mention convex instead of mastra * docs: archive use-convex-vector-search and update main specs * fix: use existing Vercel AI Gateway client for embedding generation * fix: retrieve full embedding document in aiAgentActionsKnowledge to access contentType and contentId * working AI agent and suggestions * Fixes for Typing gaps, unused ref, invalid vector filter chaining Unused ref warning in packages/convex/convex/embeddings.ts Invalid vector filter chaining in packages/convex/convex/suggestions.ts * harden types * update AGENTS.md
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Review Summary by QodoModularize Convex backend, enhance test infrastructure, and improve test reliability
WalkthroughsDescription• **Modularized Convex backend architecture**: Refactored monolithic files (testData.ts, series.ts, visitors.ts, testing/helpers.ts) into focused, domain-specific submodules, improving code organization and maintainability while preserving backward compatibility through re-exports • **Enhanced test data infrastructure**: Created comprehensive E2E test data seeding mutations (seedTour, seedSurvey, seedCarousel, seedOutboundMessage, seedArticles, seedVisitor, seedSegment, seedMessengerSettings, seedAIAgentSettings) with environment-based guards • **Improved test reliability**: Replaced implicit dialog handling with explicit confirmation button clicks in E2E tests (outbound.spec.ts, carousels.spec.ts), and removed conditional test data setup logic • **Enhanced type safety**: Improved type consistency in diagnostics script by changing messageId from string | null to Id<"messages"> | null • **Simplified test flows**: Removed redundant authentication refresh logic and replaced conditional test skipping with explicit assertions • **Documentation**: Added OpenSpec changelog entry tracking architectural changes for mobile Convex wrapper hooks Diagramflowchart LR
A["Monolithic<br/>Convex Files"] -->|Extract & Modularize| B["Domain-Specific<br/>Submodules"]
B -->|Re-export| C["Backward Compatible<br/>Public API"]
D["Implicit Dialog<br/>Handling"] -->|Replace with| E["Explicit UI<br/>Interactions"]
F["Conditional Test<br/>Setup"] -->|Simplify to| G["Direct Assertions"]
H["Generic Type<br/>Definitions"] -->|Improve to| I["Convex ID<br/>Types"]
B --> J["Enhanced Code<br/>Organization"]
C --> J
E --> K["Improved Test<br/>Reliability"]
G --> K
File Changes1. packages/convex/convex/testData.ts
|
Code Review by Qodo
1. getInternalRef uses makeFunctionReference
|
There was a problem hiding this comment.
Pull request overview
This PR refactors multiple web/mobile feature surfaces to use local, typed Convex wrapper hooks, adds new UI/domain modules (inbox/outbound/campaigns/articles), and improves CI/E2E reliability via better gating and more resilient Playwright flows.
Changes:
- Introduces new web feature modules + typed Convex wrapper hooks across outbound, inbox, help center, checklists, campaigns, and articles admin.
- Adds new domain hooks/utilities + unit tests for several inbox/editor/admin utilities.
- Hardens E2E + CI behavior (storage-state sanitization, fewer brittle selectors, and CI step summarization/gating).
Reviewed changes
Copilot reviewed 118 out of 1018 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/web/src/app/outbound/outboundMessageUi.tsx | Adds outbound message UI helpers (options, icons, badge classes). |
| apps/web/src/app/outbound/hooks/useOutboundMessagesPageConvex.ts | Adds typed Convex wrapper for outbound listing + mutations. |
| apps/web/src/app/outbound/hooks/useOutboundMessageEditorConvex.ts | Adds typed Convex wrapper for outbound editor queries/mutations. |
| apps/web/src/app/outbound/hooks/useOutboundMessageEditorController.ts | Adds controller hook coordinating editor form state + Convex calls. |
| apps/web/src/app/outbound/[id]/editorState.test.ts | Adds Vitest coverage for editor state helpers. |
| apps/web/src/app/outbound/[id]/OutboundTriggerPanel.tsx | Adds trigger configuration panel UI. |
| apps/web/src/app/outbound/[id]/OutboundStatisticsPanel.tsx | Adds statistics panel UI. |
| apps/web/src/app/outbound/[id]/OutboundPreviewPanel.tsx | Adds message preview rendering for chat/post/banner. |
| apps/web/src/app/outbound/[id]/OutboundFrequencyPanel.tsx | Adds frequency configuration panel UI. |
| apps/web/src/app/outbound/[id]/OutboundFieldLabel.tsx | Adds shared label component for outbound editor. |
| apps/web/src/app/outbound/[id]/OutboundEditorHeader.tsx | Adds editor header UI (status, preview, save, activate/pause). |
| apps/web/src/app/outbound/[id]/OutboundClickActionPanel.tsx | Adds click-action editor UI for outbound messages. |
| apps/web/src/app/onboarding/page.tsx | Migrates onboarding page to a local Convex wrapper hook. |
| apps/web/src/app/onboarding/hooks/useOnboardingConvex.ts | Adds typed Convex wrapper for hosted onboarding queries/mutations. |
| apps/web/src/app/knowledge/internal/new/page.tsx | Replaces internal-article creation page with redirect to articles flow. |
| apps/web/src/app/inbox/inboxRenderTypes.ts | Introduces shared inbox render/type contracts for hooks/components. |
| apps/web/src/app/inbox/hooks/useInboxSuggestionsCount.ts | Adds hook to load async suggestions count (sidecar). |
| apps/web/src/app/inbox/hooks/useInboxSelectionSync.ts | Adds hook to sync selection with URL query and reconcile list. |
| apps/web/src/app/inbox/hooks/useInboxSelectionSync.test.tsx | Adds unit tests for selection sync + route building. |
| apps/web/src/app/inbox/hooks/useInboxMessageActions.test.ts | Adds unit tests for optimistic last-message clearing logic. |
| apps/web/src/app/inbox/hooks/useInboxConvex.ts | Adds typed Convex wrapper for inbox data/actions. |
| apps/web/src/app/inbox/hooks/useInboxConversationListPaneConvex.ts | Adds typed Convex wrapper for visitor presence query. |
| apps/web/src/app/inbox/hooks/useInboxCompactPanels.ts | Adds compact panel state machine hook for small viewports. |
| apps/web/src/app/inbox/hooks/useInboxCompactPanels.test.tsx | Adds unit tests for compact panel behavior. |
| apps/web/src/app/inbox/hooks/useInboxAttentionCues.ts | Adds title update + sound/notification cue orchestration. |
| apps/web/src/app/inbox/hooks/useInboxAttentionCues.test.tsx | Adds unit tests for cue suppression + title changes. |
| apps/web/src/app/inbox/README.md | Documents inbox orchestration module boundaries/ownership. |
| apps/web/src/app/inbox/InboxConversationListPane.tsx | Adds conversation list pane component using new hooks/types. |
| apps/web/src/app/help/page.tsx | Migrates help center page to local Convex wrapper hooks. |
| apps/web/src/app/help/hooks/useHelpCenterConvex.ts | Adds typed Convex wrappers for help center page + article page. |
| apps/web/src/app/help/helpCenterLinks.ts | Adds helpers for building help URLs and persisting workspace id. |
| apps/web/src/app/help/[slug]/page.tsx | Migrates help article page to wrapper hooks + feedback gating. |
| apps/web/src/app/checklists/page.tsx | Migrates checklists page to typed wrapper hook + shared types. |
| apps/web/src/app/checklists/hooks/useChecklistsPageConvex.ts | Adds typed wrappers for checklists list/create/update/delete. |
| apps/web/src/app/checklists/hooks/useChecklistBuilderConvex.ts | Adds typed wrappers for checklist builder queries/mutation. |
| apps/web/src/app/checklists/checklistTypes.ts | Extracts checklist domain types to a shared module. |
| apps/web/src/app/checklists/[id]/page.tsx | Migrates checklist builder page to wrappers + shared types. |
| apps/web/src/app/campaigns/series/[id]/seriesEditorTypes.ts | Adds series editor domain types + parsing/date helpers. |
| apps/web/src/app/campaigns/series/[id]/seriesBlockUi.tsx | Adds series block UI helpers (icons/colors). |
| apps/web/src/app/campaigns/series/[id]/SeriesEditorCanvas.tsx | Adds series editor canvas rendering blocks/connections. |
| apps/web/src/app/campaigns/push/[id]/page.tsx | Migrates push campaign editor to wrapper hook; hardens stats rendering. |
| apps/web/src/app/campaigns/page.tsx | Migrates campaigns index to a consolidated wrapper hook. |
| apps/web/src/app/campaigns/hooks/useSeriesEditorConvex.ts | Adds typed wrapper hook for series editor queries/mutations. |
| apps/web/src/app/campaigns/hooks/usePushCampaignEditorConvex.ts | Adds typed wrapper hook for push campaign editor queries/mutations. |
| apps/web/src/app/campaigns/hooks/useEmailCampaignEditorConvex.ts | Adds typed wrapper hook for email campaign editor queries/mutations. |
| apps/web/src/app/campaigns/hooks/useCarouselEditorConvex.ts | Adds typed wrapper hook for carousel editor queries/mutations. |
| apps/web/src/app/campaigns/hooks/useCampaignsPageConvex.ts | Adds typed wrapper hook consolidating campaigns list/actions. |
| apps/web/src/app/campaigns/email/[id]/page.tsx | Migrates email campaign editor to wrapper hook; hardens stats rendering. |
| apps/web/src/app/campaigns/carousels/[id]/page.tsx | Migrates carousel editor to wrapper hook; hardens stats rendering. |
| apps/web/src/app/audit-logs/page.tsx | Fixes AuditLogViewer import path. |
| apps/web/src/app/articles/hooks/useArticlesAdminConvex.ts | Adds typed wrapper hook for articles admin flows (import/export). |
| apps/web/src/app/articles/hooks/useArticleEditorConvex.ts | Adds typed wrapper hook for article editor + assets management. |
| apps/web/src/app/articles/hooks/useArticleCollectionsConvex.ts | Adds typed wrapper hook for collections admin page actions. |
| apps/web/src/app/articles/collections/page.tsx | Migrates collections page to wrapper hook; guards articleCount usage. |
| apps/web/src/app/articles/articlesAdminUtils.ts | Adds admin utilities for filtering + import signature building. |
| apps/web/src/app/articles/articlesAdminUtils.test.ts | Adds Vitest coverage for filtering behavior. |
| apps/web/src/app/articles/articlesAdminTypes.ts | Adds typed contracts for articles admin UI + import/export. |
| apps/web/src/app/articles/DeleteArticleDialog.tsx | Adds a modal confirmation dialog component for deletes. |
| apps/web/package.json | Updates lint command, Convex version, and adds web-shared dep. |
| apps/web/next.config.js | Transpiles @opencom/web-shared for Next build compatibility. |
| apps/web/e2e/widget.spec.ts | Makes widget selectors more resilient; clears volatile storage keys; skips flaky dismiss test. |
| apps/web/e2e/tooltips.spec.ts | Improves workspace id discovery + deterministic confirm flow. |
| apps/web/e2e/snippets.spec.ts | Converts auth guard from skip to assertion failure. |
| apps/web/e2e/reports.spec.ts | Simplifies auth refresh usage. |
| apps/web/e2e/public-pages.spec.ts | Removes nondeterministic restricted-boundary E2E path and documents why. |
| apps/web/e2e/outbound.spec.ts | Moves delete confirmation to UI confirm button flow. |
| apps/web/e2e/inbox.spec.ts | Extracts convert-to-ticket retry helper; tightens suggestions expectations. |
| apps/web/e2e/helpers/widget-helpers.ts | Adds helper waits and improves visibility checks for tours/surveys/AI UI. |
| apps/web/e2e/helpers/storage-state.ts | Adds sanitization utilities to remove volatile localStorage from storageState. |
| apps/web/e2e/fixtures.ts | Refreshes auth and sanitizes storage state before/after worker contexts. |
| apps/web/e2e/csat.spec.ts | Makes visitor identity deterministic via visitorKey + email derivation. |
| apps/web/e2e/carousels.spec.ts | Removes redundant auth refresh; switches deletes to confirm-button flow. |
| apps/web/e2e/auth.spec.ts | Makes signup button selection resilient to UI wording changes. |
| apps/web/e2e/ai-agent-settings.spec.ts | Improves settings navigation + deterministic section expansion. |
| apps/web/e2e/SKIP_REGISTRY.md | Updates skip registry entries after E2E refactors. |
| apps/mobile/src/typeHardeningGuard.test.ts | Adds guard tests preventing direct convex/react usage outside approved boundaries. |
| apps/mobile/src/lib/convex/hooks.ts | Adds typed mobile adapter hooks around convex/react. |
| apps/mobile/src/hooks/convex/useSettingsConvex.ts | Adds typed wrapper hook for mobile settings flows. |
| apps/mobile/src/hooks/convex/useOnboardingConvex.ts | Adds typed wrapper hook for mobile onboarding flows. |
| apps/mobile/src/hooks/convex/useNotificationRegistrationConvex.ts | Adds typed wrapper hook for push token registration/debugging. |
| apps/mobile/src/hooks/convex/useInboxConvex.ts | Adds typed wrapper hook for mobile inbox listing + presence. |
| apps/mobile/src/hooks/convex/useConversationConvex.ts | Adds typed wrapper hook for mobile conversation thread flows. |
| apps/mobile/src/hooks/convex/useAuthConvex.ts | Adds typed wrapper hook for mobile auth context + onboarding routing. |
| apps/mobile/src/hooks/convex/types.ts | Adds typed mobile Convex records used by wrapper hooks and UI. |
| apps/mobile/src/contexts/NotificationContext.tsx | Migrates to wrapper hook; memoizes debug logging helper. |
| apps/mobile/src/contexts/AuthContext.tsx | Migrates to wrapper hooks and shared mobile Convex types. |
| apps/mobile/package.json | Updates Convex version. |
| apps/mobile/app/_layout.tsx | Adjusts route segment detection for auth navigation guard. |
| apps/mobile/app/(app)/settings.tsx | Migrates to wrapper hook for settings data/actions. |
| apps/mobile/app/(app)/onboarding.tsx | Migrates to wrapper hook for onboarding flows. |
| apps/mobile/app/(app)/index.tsx | Migrates inbox screen to wrapper hooks + shared mobile types. |
| apps/mobile/app/(app)/conversation/[id].tsx | Migrates conversation screen to wrapper hook + shared message type. |
| CONTRIBUTING.md | Adds Convex type-safety playbook + updates frontend/mobile guidance. |
| .github/workflows/ci.yml | Adds continue-on-error + summarization step that gates only critical failures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function parseRuleText( | ||
| value: string, | ||
| label: string | ||
| ): { value?: Record<string, unknown>; error?: string } { | ||
| const trimmed = value.trim(); | ||
| if (!trimmed) { | ||
| return { value: undefined }; | ||
| } | ||
|
|
||
| try { | ||
| const parsed = JSON.parse(trimmed) as Record<string, unknown>; | ||
| return { value: parsed }; | ||
| } catch { | ||
| return { error: `${label} must be valid JSON.` }; | ||
| } | ||
| } |
| function renderPreview( | ||
| messageType: MessageType, | ||
| content: MessageContent, | ||
| postButtonForm: PostButtonFormState | ||
| ) { | ||
| switch (messageType) { | ||
| case "chat": | ||
| return ( | ||
| <div className="bg-white rounded-lg shadow-lg p-4 max-w-sm"> | ||
| <div className="flex items-start gap-3"> | ||
| <div className="w-8 h-8 rounded-full bg-primary/50 flex items-center justify-center text-white text-sm"> | ||
| {content.senderId ? "A" : "B"} | ||
| </div> | ||
| <div className="flex-1"> | ||
| <div className="bg-gray-100 rounded-lg p-3"> | ||
| <p className="text-sm">{content.text || "Your message here..."}</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
|
|
||
| case "post": { | ||
| const postPreviewButtons = getPostPreviewButtons(postButtonForm); | ||
|
|
||
| return ( | ||
| <div className="bg-white rounded-lg shadow-lg max-w-md overflow-hidden"> | ||
| {content.imageUrl && <img src={content.imageUrl} alt="" className="w-full h-40 object-cover" />} | ||
| {content.videoUrl && ( | ||
| <video src={content.videoUrl} controls className="w-full max-h-64 bg-black" /> | ||
| )} | ||
| <div className="p-6"> | ||
| <h3 className="text-xl font-bold mb-2">{content.title || "Title"}</h3> | ||
| <p className="text-gray-600">{content.body || "Your announcement content..."}</p> | ||
| {postPreviewButtons.length > 0 && ( | ||
| <div className="mt-4 flex gap-2"> | ||
| {postPreviewButtons.map((button, index) => | ||
| button.variant === "secondary" ? ( | ||
| <Button key={`${button.text}-${index}`} size="sm" variant="outline"> | ||
| {button.text} | ||
| </Button> | ||
| ) : ( | ||
| <Button key={`${button.text}-${index}`} size="sm"> | ||
| {button.text} | ||
| </Button> | ||
| ) | ||
| )} | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| case "banner": | ||
| return ( | ||
| <div | ||
| className={`${content.style === "floating" ? "shadow-lg" : ""} bg-primary text-white p-3 flex items-center justify-between`} | ||
| > | ||
| <p className="text-sm font-medium">{content.text || "Your banner text..."}</p> | ||
| {content.dismissible !== false && ( | ||
| <button | ||
| type="button" | ||
| className="text-white/80 hover:text-white" | ||
| aria-label="Dismiss banner" | ||
| > | ||
| x | ||
| </button> | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
| export function getSeriesBlockIcon(type: BlockType): React.JSX.Element { | ||
| switch (type) { | ||
| case "rule": | ||
| return <Filter className="h-4 w-4" />; | ||
| case "wait": | ||
| return <Clock className="h-4 w-4" />; | ||
| case "email": | ||
| return <Mail className="h-4 w-4" />; | ||
| case "push": | ||
| return <Bell className="h-4 w-4" />; | ||
| case "chat": | ||
| return <MessageSquare className="h-4 w-4" />; | ||
| case "post": | ||
| return <MessageSquare className="h-4 w-4" />; | ||
| case "carousel": | ||
| return <GitBranch className="h-4 w-4" />; | ||
| case "tag": | ||
| return <Tag className="h-4 w-4" />; | ||
| } | ||
| } |
| export function useInboxSuggestionsCount({ | ||
| selectedConversationId, | ||
| isSidecarEnabled, | ||
| messageCountSignal, | ||
| getSuggestionsForConversation, | ||
| }: UseInboxSuggestionsCountArgs): UseInboxSuggestionsCountResult { | ||
| const [suggestionsCount, setSuggestionsCount] = useState(0); | ||
| const [isSuggestionsCountLoading, setIsSuggestionsCountLoading] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| let cancelled = false; |
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
| function getInternalRef(name: string): unknown { | ||
| return makeFunctionReference(name); | ||
| } |
There was a problem hiding this comment.
1. getinternalref uses makefunctionreference 📘 Rule violation ⛯ Reliability
makeFunctionReference(...) is executed inside getInternalRef, so it runs per-call rather than once at module load time. This violates the requirement to create function references at module scope only and can introduce per-invocation overhead/instability.
Agent Prompt
## Issue description
`makeFunctionReference(name)` is called inside `getInternalRef()`, which violates the rule that `makeFunctionReference(...)` must only be executed at module scope.
## Issue Context
This code is an admin gateway that dispatches to internal test mutations; it currently creates refs dynamically per invocation.
## Fix Focus Areas
- packages/convex/convex/testAdmin.ts[15-39]
- packages/convex/convex/testAdmin.ts[61-96]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| report_blocking "Lint" "${{ steps.lint.outcome }}" | ||
| report_blocking "Typecheck" "${{ steps.typecheck.outcome }}" | ||
| report_blocking "Convex raw auth guard" "${{ steps.convex_auth_guard.outcome }}" | ||
| report_warning "Convex validator any guard" "${{ steps.convex_any_guard.outcome }}" | ||
| report_blocking "Secret scan gate" "${{ steps.secret_scan.outcome }}" |
There was a problem hiding this comment.
7. Any-args gate nonblocking 🐞 Bug ⛨ Security
.github/workflows/ci.yml runs the Convex v.any() guard with continue-on-error and then reports it via report_warning, so the job does not fail even when the gate script exits with code 1. This allows undocumented or expired v.any() validator usage to merge without CI blocking.
Agent Prompt
### Issue description
The CI workflow downgrades the `pnpm security:convex-any-args-gate` step to warning-only, so violations no longer fail the `checks` job even though the script exits with code 1.
### Issue Context
This removes enforcement for the repository’s v.any() exception registry policy and allows undocumented/expired exceptions to land without CI blocking.
### Fix Focus Areas
- .github/workflows/ci.yml[49-52]
- .github/workflows/ci.yml[109-113]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
This pull request introduces several improvements to both the codebase and developer documentation, with a focus on enhancing type safety, maintainability, and CI reliability. The most significant changes are the refactoring of Convex usage in the mobile app to use local wrapper hooks, updates to type safety guidelines and documentation, and enhancements to the CI workflow to better summarize and gate check results.
Mobile app Convex integration refactor:
convex/reactandapiusage inapps/mobile/app/(app)/conversation/[id].tsxandapps/mobile/app/(app)/index.tsxto new local wrapper hooks (useConversationConvex,useInboxConvex,useVisitorPresenceConvex), improving type safety and encapsulation. Direct imports of Convex hooks and types were removed in favor of shared, strongly-typed adapters. (apps/mobile/app/(app)/conversation/[id].tsxL14-R16, apps/mobile/app/(app)/conversation/[id].tsxL38-R45, apps/mobile/app/(app)/conversation/[id].tsxL74-R63, apps/mobile/app/(app)/conversation/[id].tsxL93-R78, [1] [2]Type safety and documentation:
CONTRIBUTING.mdguidelines to require usage of local Convex wrapper hooks and adapters for all new code, both in web and mobile. The guidelines now discourage direct use of generic Convex factories and promote keeping workarounds local when type errors occur. [1] [2]convex/reactimports in runtime UI modules, including for mobile.CI workflow improvements:
.github/workflows/ci.ymlto addcontinue-on-error: trueandidfields to each step, and introduced a new "Summarize check results" step that reports pass/fail status for each check, blocking the workflow only if critical checks fail. This provides clearer feedback and more robust gating of deployments.Roadmap and planning updates:
ROADMAP.mdto clarify and reprioritize outstanding tasks, including CI dependencies, type safety migration, and mobile parity. Added detailed notes on Convex type safety migration hotspots and batching/performance refactor targets, as well as expanded the feature and SDK planning sections. [1] [2] [3]These changes collectively improve developer experience, code maintainability, and project reliability.